SOFTWARE DESIGN · JEV
EVALUATION · TYPESAFE JEV · MIRO STATE CHECK

Jev tells flawed types from clean ones, cannot name redundant or overloaded states, and the condensed article does not change that

Given the domain's required states, it sorts 84% of TypeScript types correctly into flawed or compliant and names illegal and missing states reliably. Redundant and overloaded encodings it mostly waves through as compliant or mislabels as missing. Sending a 695-word MIRO reference in the state moves the bias, not the accuracy.

jev-1.13.0 · 1152 REQUESTS · 24 DOMAINS · 2 REFERENCE ARMS · 1 CHOICE + 4 NOUL PER REQUEST · 2026-09-17
5-WAY · NO ARTICLE
59.2%
SPEC 68 · BLIND 51
5-WAY · WITH ARTICLE
58.5%
SPEC 65 · BLIND 52
FLAW FOUND · SPEC
83.7%
RECALL 89 · PREC 87
SUBTLE FLAWS · SPEC
39.7%
OBVIOUS 82
REQUESTS
1152
576 PER ARM · 24 DOMAINS
COST
0.092USD
2192K TOK · P50 285 MS
WHAT WAS TESTEDCORPUS · AXES · QUESTIONS

Corpus. 24 everyday domains (an inbox loading, a traffic light, an order, a WebSocket, a cinema seat…). Each has a written list of the abstract states it must distinguish, two hand-written compliant encodings, and one flawed encoding per MIRO letter. Every flawed snippet carries exactly one flaw, graded obvious or subtle. That gives 144 base snippets.

Axes. Each snippet is sent eight times. spec puts the required-states list in the state; blind gives only a one-line domain description. plain sends the type alone; noise prepends an unrelated well-formed type. none defines MIRO only in the question text; article adds a condensed version of the blog post as a miro_reference field in the state. Same snippet, eight cells: every comparison is paired.

Questions. One request per sample carrying a five-option choice (compliant plus the four letters, each with what, not_for and examples) and four independent noul detectors, one per letter. Ground truth is the authored label; a verdict is correct only if it names the exact class.

ClassDefinition used in the promptN per arm
compliantEvery abstract state has exactly one concrete value and vice versa.192
missingAn abstract state, or data a state must carry, has no representation.96
illegalThe type admits a value or field combination with no meaning.96
redundantTwo distinct concrete values encode the same abstract state.96
overloadedOne concrete value stands for two abstract states the domain must tell apart.96
ONE REQUEST, VERBATIMorder.overloaded.spec.plain · 332 MS · 1424 TOK IN

What Jev sees. In the none arm the blog post is not in the request. MIRO reaches the model only through the question text below: one instruction line, then a what, a not_for and two examples per letter, written by hand from the post. The state carries the domain, the required-states list (spec condition only), the type and one note about branded aliases. Nothing else. The article arm sends the identical request plus one extra state field, miro_reference, whose full text is in the next panel.

This sample. Authored label overloaded: 'open' stands for both 'awaiting payment' and 'paid, awaiting shipment'. Jev answers missing at 0.96 without the article and missing at 0.88 with it.

REQUEST · POST api.typesafe.ai/v1/systemoneAUTHORIZATION: BEARER …
{
  "state": {
    "domain": "Lifecycle of a customer order in a web shop",
    "required_states": [
      "Placed, awaiting payment",
      "Paid, awaiting shipment",
      "Shipped, with a carrier tracking number",
      "Delivered",
      "Cancelled, with the reason recorded"
    ],
    "type_definition": "type Order =\n  | { status: 'open' }\n  | { status: 'shipped'; tracking: TrackingNumber }\n  | { status: 'delivered' }\n  | { status: 'cancelled'; reason: string };",
    "notes": "Branded aliases (UserId, Celsius, Percent, Milliseconds, Url, Cents, ...) are defined elsewhere."
  },
  "model": "jev-latest",
  "questions": {
    "verdict": {
      "type": "choice",
      "instructions": "Judge `type_definition` against the MIRO checklist for state modelling. The abstract states are the situations the domain in `domain` must distinguish (listed in `required_states` when present). The concrete states are the values the TypeScript type admits. A compliant type maps abstract states one-to-one onto concrete values. Pick the single most serious problem, or `compliant` if the mapping is one-to-one. `notes` explains helper aliases; do not treat them as problems.",
      "criteria": {
        "compliant": {
          "what": "Every abstract state has exactly one concrete value and every concrete value means exactly one abstract state.",
          "not_for": "Types that merely look tidy; check each required situation is expressible and no field combination is meaningless."
        },
        "missing": {
          "what": "An abstract state the domain needs cannot be expressed at all by the type, or a piece of data a state must carry has nowhere to live.",
          "not_for": "States that are expressible but share a value with another state (that is overloaded).",
          "examples": [
            "No branch for 'cancelled' although orders can be cancelled",
            "A 'read' state that must record a time but has no time field"
          ]
        },
        "illegal": {
          "what": "The type admits a combination of field values that has no meaning in the domain, typically independent optional fields or booleans that should be mutually exclusive.",
          "not_for": "Union types whose branches each carry only the data that branch needs.",
          "examples": [
            "{ loading: true, error: 'x', data: [...] } all at once",
            "Three independent boolean lamps that can all be true"
          ]
        },
        "redundant": {
          "what": "Two or more distinct concrete values encode the same abstract state: duplicate variant names, two fields that restate one fact, or a field whose value is ignored in some branch.",
          "not_for": "Distinct variants that only look similar but mean different situations.",
          "examples": [
            "Both 'cancelled' and 'canceled' variants",
            "position in ms and in seconds side by side",
            "A target temperature carried in the 'off' branch"
          ]
        },
        "overloaded": {
          "what": "One concrete value stands for two or more abstract states that the domain must tell apart; the reader cannot recover which one is meant.",
          "not_for": "A value that genuinely means one thing even if the name is vague.",
          "examples": [
            "null meaning both 'not asked yet' and 'explicitly off'",
            "A 'stopped' variant covering both 'paused' and 'finished'"
          ]
        }
      }
    },
    "has_missing": {
      "type": "noul",
      "instructions": "Does `type_definition` exhibit the MIRO 'missing' problem for the domain in `domain`? An abstract state or required piece of data has no representation.",
      "criteria": {
        "true": "Yes: at least one clear instance of the 'missing' problem is present.",
        "false": "No: the type is free of the 'missing' problem, whatever other problems it may have."
      }
    },
    "has_illegal": {
      "type": "noul",
      "instructions": "Does `type_definition` exhibit the MIRO 'illegal' problem for the domain in `domain`? The type admits a value or field combination with no meaning in the domain.",
      "criteria": {
        "true": "Yes: at least one clear instance of the 'illegal' problem is present.",
        "false": "No: the type is free of the 'illegal' problem, whatever other problems it may have."
      }
    },
    "has_redundant": {
      "type": "noul",
      "instructions": "Does `type_definition` exhibit the MIRO 'redundant' problem for the domain in `domain`? Two distinct concrete values encode the same abstract state.",
      "criteria": {
        "true": "Yes: at least one clear instance of the 'redundant' problem is present.",
        "false": "No: the type is free of the 'redundant' problem, whatever other problems it may have."
      }
    },
    "has_overloaded": {
      "type": "noul",
      "instructions": "Does `type_definition` exhibit the MIRO 'overloaded' problem for the domain in `domain`? One concrete value stands for two different abstract states.",
      "criteria": {
        "true": "Yes: at least one clear instance of the 'overloaded' problem is present.",
        "false": "No: the type is free of the 'overloaded' problem, whatever other problems it may have."
      }
    }
  }
}
RESPONSE · HTTP 200jev-1.13.0
{
  "model": "jev-1.13.0",
  "answers": {
    "verdict": {
      "type": "choice",
      "choice": "missing",
      "confidence": 0.95,
      "probabilities": {
        "overloaded": 0.04,
        "compliant": 0.0,
        "illegal": 0.0,
        "redundant": 0.0,
        "missing": 0.96
      }
    },
    "has_missing": {
      "type": "noul",
      "noul": 0.95
    },
    "has_illegal": {
      "type": "noul",
      "noul": 0.57
    },
    "has_redundant": {
      "type": "noul",
      "noul": 0.19
    },
    "has_overloaded": {
      "type": "noul",
      "noul": 0.51
    }
  },
  "usage": {
    "input_tokens": 1424,
    "output_tokens": 135
  }
}
WITH VS WITHOUT THE ARTICLE576 PAIRED REQUESTS PER ARM · Δ = ARTICLE − NONE
MetricNoneArticleΔ
5-way · all5959−1
5-way · spec6865−3
5-way · blind5152+1
5-way · spec, 20 domains not in the article6664−2
5-way · spec, 4 domains taken from the article7771−6
flaw found · spec recall8988−1
compliant kept · spec7470−4
recall compliant · spec7470−4
recall missing · spec98100+2
recall illegal · spec8883−4
recall redundant · spec3850+12
recall overloaded · spec3517−19
recall compliant · blind9946−53
recall missing · blind056+56
recall illegal · blind7792+15
recall redundant · blind2542+17
recall overloaded · blind431+27
subtle flaws · spec4031−9
ECE · all0.1540.081−0.073
input tokens per request14122394+982
latency p50 ms286284−2

Paired by sample: both right 259, right only without the article 82, right only with it 78, both wrong 157. The article flips 160 verdicts and the flips cancel.

What moved: with the article, blind requests stop defaulting to compliant (recall 99 → 46) and start finding missing states they cannot actually know about (recall 0 → 56). Under spec, overloaded is read as missing even more often. The reference teaches Jev the word "missing", not the distinction.

STATE.MIRO_REFERENCE · SENT VERBATIM IN THE ARTICLE ARM695 WORDS
MIRO reference for judging a data model (condensed from "State of Emergency", note89.github.io/state-of-emergency).

WHAT STATE IS. There are four levels: the real world; the abstract state, which is the set of situations the program must tell apart (for an inbox: "not requested yet", "request in flight", "loaded with zero or more messages", "failed with an error to show"); the concrete state, which is the type that represents those situations; and the interpretation, the code that reads the type. A data model is correct when abstract and concrete states map one-to-one: every situation has exactly one value, and every value means exactly one situation. Every departure from one-to-one is one of four MIRO problems.

HOW TO CHECK A TYPE.
1. List the abstract states the domain must distinguish (given in required_states when present, otherwise inferred from the domain).
2. Enumerate the values the type admits, including every combination of optional fields, booleans and nulls.
3. For each abstract state ask: how many values represent it? None: Missing. Two or more: Redundant.
4. For each admitted value ask: which abstract state does it mean? None: Illegal. Two or more: Overloaded.
5. Helper aliases and branded primitives (UserId, Celsius, Percent, Milliseconds) are not problems; only the mapping counts.

M - MISSING. An abstract state exists but the type cannot express it, or data a state must carry has nowhere to live. How to spot it: a plain requirement that is impossible to represent with the type as written. Example: the inbox must show "not requested yet", but
  type Inbox = { kind: 'loading' } | { kind: 'loaded'; messages: Message[] } | { kind: 'failed'; error: string }
has no such branch. Fix: add a unique concrete state for it.

I - ILLEGAL. The type admits a value that has no meaning in the domain. Typical shapes: independent optional fields or booleans that should be mutually exclusive, or options that "only apply if X is on" but can be set anyway. Example:
  type Inbox = { loading: boolean; messages?: Message[]; error?: string }
admits loading true with messages and error both set, which means nothing. Example:
  type Settings = { extraFxOn: boolean; reverb: Percent; delay: Milliseconds }
admits reverb and delay while effects are off. Fix: remove the meaningless values with a discriminated union whose branches carry only the data that branch needs:
  type ExtraFx = { kind: 'off' } | { kind: 'on'; reverb: Percent; delay: Milliseconds }

R - REDUNDANT. Two or more distinct values encode the same abstract state. Typical shapes: two variant names for one situation ('off' and 'disabled'; 'cancelled' and 'canceled'); two fields that restate one fact (a position in milliseconds and in seconds side by side); a field whose value is ignored in some branch (a reverb amount kept while effects are off: every reverb value encodes the same "off"). How to spot it: changing the value changes nothing observable. Fix: remove the duplicate encoding so one situation has one value.

O - OVERLOADED. One value stands for two abstract states the domain must tell apart, and the reader cannot recover which one is meant. Typical shapes: null, undefined or an empty array meaning both "not asked yet" and "empty" or "explicitly off"; one variant name covering two situations that need different handling. How to spot it: the UI shows "no messages" and then messages appear a moment later, because "empty" and "not loaded yet" shared one value. Fix: split the value into explicit variants:
  type Inbox = { kind: 'not-asked' } | { kind: 'loading' } | { kind: 'success'; messages: Message[] } | { kind: 'failure'; error: string }

TELLING THEM APART. If a situation cannot be expressed at all, it is Missing; if it can be expressed but shares its value with another situation, it is Overloaded. An Illegal value means nothing; a Redundant value means the same as another value. A type is compliant only when every required situation has exactly one value and no admitted value is meaningless or ambiguous. The strongest fix is always to make the concrete type stricter so the bad states become unrepresentable, rather than relying on the interpretation code to avoid them.
WHERE VERDICTS LANDROWS = TRUTH · COLS = JEV · SHADE = SHARE OF ROW
SPEC · NO ARTICLE
SPEC · WITH ARTICLE
BLIND · NO ARTICLE
BLIND · WITH ARTICLE

With the spec, missing is caught 98% of the time; blind, 0%. A reviewer cannot know what is missing without the list of what must exist, and neither can Jev.

Overloaded types are read as missing 30 of 48 times under spec without the article: a value that means two things does lose a distinction, so the confusion is between two true descriptions of one type. Redundant types pass as compliant 18 of 48 times: duplicates that add no illegal value look harmless. Panels below this one use the no-article arm.

PER CLASS · SPECNO ARTICLE · N = 288
TruthNPrec %Recall %F1State
compliant9676740.75warn
missing4853980.69warn
illegal48100880.93ok
redundant48100380.55warn
overloaded4837350.36crit
PER CLASS · BLINDNO ARTICLE · N = 288
TruthNPrec %Recall %F1State
compliant9642990.59warn
missing48000.00crit
illegal4892770.84ok
redundant4875250.38crit
overloaded4810040.08crit
CONDITION × PRESENTATIONNO ARTICLE · 144 PER CELL
CondPres5-way accuracyFlaw recall %Compliant kept %
specplain
70%
8979
specnoise
65%
8969
blindplain
51%
31100
blindnoise
50%
3298

The spec is worth about 17 points; the distractor type costs about 3. Naming the states matters far more than keeping the input clean.

DIFFICULTYNO ARTICLE · AUTHORED TIER
TierTypical flawNSpecAll
compliantNo flaw; both encodings per domain192
74%
86%
obviousWhole variant absent, duplicate variant name, independent booleans228
82%
61%
subtleIgnored field in one branch, two units for one fact, null with two meanings156
40%
24%

Obvious flaws (a variant that is simply absent, three independent booleans) are found. Subtle ones (a field that is ignored in one branch, a null that means two things) mostly are not.

NOUL DETECTORS · SPECNO ARTICLE · ROC PER LETTER · HOVER FOR THRESHOLD
DetectorAUC specAUC allBest cutPrec %Recall %State
has_missing0.940.760.936943ok
has_illegal0.980.950.716982ok
has_redundant0.720.710.463257warn
has_overloaded0.810.630.634532ok

A noul is a probability of yes; the ROC shows every cut at once. The best cut and its precision and recall are computed on all 576 samples of the arm, so treat them as an upper bound: they were picked on the same data they are scored on.

Positive = the sample's authored flaw is that letter. Negatives include compliant types and the other three flaws.

CALIBRATION · CHOICE CONFIDENCENO ARTICLE · ECE 0.154

Confidence is how concentrated the probability mass is, not a promise of accuracy. Above 0.9 it is worth something; between 0.3 and 0.9 accuracy is flat.

CALIBRATION TABLENO ARTICLE · ALL 576
BinNMean conf %Accuracy %
0.1–0.241725
0.2–0.3372538
0.3–0.4563554
0.4–0.5704561
0.5–0.6625453
0.6–0.7846554
0.7–0.8817560
0.8–0.9718555
0.9–1.01119578
PER DOMAIN24 PER DOMAIN PER ARM · 12 PER CONDITION
DomainDescriptionSpecBlindSpec + articleMissed classes (no article)
traffic_lightA road traffic light controller · in article
100%
83%
50%
missing ×2
media_playerPlayback state of an audio player
83%
67%
67%
overloaded ×4, missing ×2
calendar_eventScheduling of a calendar event
75%
67%
67%
overloaded ×3, compliant ×2, missing ×2
ovenA kitchen oven's control state · in article
83%
50%
75%
redundant ×4, missing ×2, overloaded ×2
uploadUploading a single file from a form
83%
50%
75%
overloaded ×4, redundant ×2, missing ×2
invoiceAn invoice sent to a client
67%
67%
75%
compliant ×3, overloaded ×3, missing ×2
form_fieldValidation state of a single text field in a form
83%
50%
83%
overloaded ×4, missing ×2, redundant ×2
discountDiscount applied to a shopping cart
75%
50%
75%
redundant ×4, overloaded ×3, missing ×2
feature_flagRollout state of a feature flag
83%
42%
83%
redundant ×4, missing ×2, overloaded ×2, illegal ×1
audio_fxExtra audio effects setting in a music player · in article
67%
50%
83%
illegal ×4, overloaded ×2, compliant ×2, missing ×2
auth_sessionBrowser session for a web app
67%
50%
50%
redundant ×4, missing ×2, overloaded ×2, illegal ×1, compliant ×1
orderLifecycle of a customer order in a web shop
67%
50%
83%
overloaded ×4, compliant ×2, missing ×2, illegal ×2
shipping_addressShipping address selection at checkout
67%
50%
67%
overloaded ×4, redundant ×4, missing ×2
elevatorAn elevator car
67%
50%
67%
redundant ×4, overloaded ×4, missing ×2
message_listLoading a user's message inbox from the server · in article
58%
50%
75%
overloaded ×4, redundant ×4, missing ×3
door_lockA smart door lock
67%
42%
75%
redundant ×4, compliant ×3, missing ×2, overloaded ×2
background_jobA background job in a task queue
75%
33%
83%
overloaded ×4, illegal ×3, missing ×2, redundant ×2
thermostatA home thermostat schedule entry
67%
33%
67%
redundant ×4, illegal ×4, missing ×2, overloaded ×2
chat_messageDelivery state of an outgoing chat message
50%
50%
42%
overloaded ×4, redundant ×4, missing ×2, compliant ×2
search_boxA search box with server-side results
50%
50%
33%
overloaded ×4, redundant ×4, compliant ×2, missing ×2
ws_connectionA WebSocket connection managed by a client library
50%
50%
50%
redundant ×4, overloaded ×4, compliant ×2, missing ×2
seat_bookingA seat in a cinema booking system
50%
50%
33%
overloaded ×4, redundant ×4, missing ×2, compliant ×2
pr_reviewReview state of a pull request
42%
50%
50%
redundant ×4, overloaded ×4, compliant ×3, missing ×2
subscriptionA SaaS subscription's billing state
50%
33%
50%
redundant ×4, overloaded ×4, missing ×2, illegal ×2, compliant ×2
EVERY SAMPLECLICK A ROW FOR THE TYPE, THE AUTHORED REASON AND THE FULL DISTRIBUTION
IDTruthVerdictConfRefTierCondPres
CAVEATSREAD BEFORE QUOTING A NUMBER
  • The corpus and its labels were written by Claude in one sitting, not sampled from real code. The flaw taxonomy follows the MIRO article; the tier grading is an author's judgment.
  • The first four domains (message_list, traffic_light, oven, audio_fx) are the article's own examples, and the reference text reuses them. The comparison table therefore also reports the 20 other domains on their own.
  • Each flawed snippet has one intended flaw. Some arguably carry a second one; the choice question asks for the most serious, and a defensible second reading is scored as wrong.
  • Classes are not balanced: 192 compliant, 96 per flaw, per arm. Binary flaw-detection numbers are the ones to compare across conditions.
  • Under blind, "missing" has no fair answer; those 96 samples cap the blind accuracy.
  • One prompt design, one run per arm, no retries on disagreement. Prompt wording moves these numbers; nothing here was tuned against the results.
  • Single-request latency and cost at concurrency 8; rate limits were not approached.