Jev Evaluation Loop for the Peregrine Item Bank

Phase 0 complete, 20 September 2026 — findings F1–F3 below, two of which change the plan. A confidence-gated judging pass over Peregrine's 439 multiple-choice items using TypeSafe's Jev model, with thresholds that route a confident failure to Claude and an uncertain read to you.

What Jev is. typesafe/jev-1.13 is a "System One" model. It takes a state and a map of typed questions, evaluates every question in parallel against that state in one request, and returns typed answers with calibrated probabilities. It does not generate text. Three question types: Noul (yes/no probability), Choice (one option from a set, with a probability per option), Score (a rating against ordered level descriptions). Choice and Score carry a confidence derived from how concentrated the probability distribution is. Noul does not. One wrinkle the OpenAPI schema settles: confidence and probabilities are not required fields, so a thin answer must read as absent rather than average.

How we reach it. Through OpenRouter's Decisions endpoint, not api.typesafe.ai. The native typed shape survives intact.

POST https://openrouter.ai/api/alpha/decisions
Authorization: Bearer <OpenRouter key>

{ "model": "typesafe/jev-1.13", "state": …, "questions": {…} }

Two things about this route cost a wrong turn to learn. The model does not appear in GET /api/v1/models — that listing carries 446 chat models across 61 providers and Jev is in none of them, which makes "OpenRouter cannot serve Jev" a natural and wrong conclusion. A Choice's criteria is a {label: description} record, never an options array; the array form has returned 503s in production elsewhere, which is a confusing way for a schema error to present. Field names were read from the OpenAPI operation createApiAlphaDecisions rather than guessed.

What it costs — measured, no longer estimated. A three-question call on a real Wager item cost $0.000027 at 643 input tokens. 260 calls across the full math bank cost $0.0053. A full 439-item pass lands near four cents, confirming the estimate. Round trips ran 0.31s. The OpenRouter key carries a $10 monthly limit, roughly 285 full-bank passes a month.

This plan originally scoped math answer-correctness out, and Phase 0 overturned that. TypeSafe's limitations page says Jev is not a calculator, and on that basis the first draft cut answer-key checking for the 130 math items. That warning is about arithmetic Jev is asked to perform. Judging whether a supplied key is right is a different task, and it survives — see F1.

Findings · Phases 0 to 2

F1  Jev judges math answer keys far better than the vendor warning predicted

Paired probe over all 130 math items, each asked twice — once about its real answer key, once about a deliberately wrong one. Each item is its own baseline, so item difficulty cannot inflate one arm. 130/130 answered, $0.0053.

meanmedian
noul on the real key0.8260.905
noul on a wrong key0.2680.100
paired separation+0.558

Real scored above wrong on 116 of 130 items. At a 0.6 threshold it accepts 90% of real keys and rejects 85% of wrong ones.

The original scoping decision was too cautious. Math answer-correctness returns as a candidate dimension.

F2  Where it fails is the more useful half of the result

The eight worst-separating items name their own trap families: dataread-wrongtarget, solve-msign, math-targetcheck, math-units, setup-minvert, solve-mpartial. Every one is a distractor that is correct arithmetic for a slightly different reading of the stem. problem-solving-data is the weakest skill at +0.467, and units and wrong-target traps are where it is weakest within that.

That is the literal-reading failure mode TypeSafe documents, landing exactly where the item design intends it to. It is also the guide's own difficulty criterion A — "an intermediate on the answer sheet, so the value that looks like the answer is not the one the stem asked for."

A distractor that fools Jev is a distractor doing its job. Low separation reads as a signal about the item rather than only about the model, which makes it a candidate input to difficulty rather than a failure to queue for revision.
  • Decide whether F2 becomes its own dimension, and whether a low-separation item should be flagged as well-authored rather than sent to Claude

F3  Only 36% of the bank carries the labels this rubric was built to audit

FieldPresentMissingCoverage
skill_code15828136%
difficulty15828136%
difficulty_basis15828136%

Missing skill_code by section: reading 134 of 178, math 96 of 130, writing 51 of 131.

This is the finding most likely to reshape the plan. For 158 items Jev audits a label that exists. For 281 items there is nothing to disagree with, so Jev supplies a label that was never authored. The second is arguably worth more than the first, and Phases 3 and 4 are not written for it.

It also caps calibration: agreement rates for skill_code and difficulty can only be computed on the 158 labelled items, and the 40-item sample has to be drawn from those.

  • Decide whether backfill becomes a first-class mode beside audit, with its own confidence floor — a proposed label crossing into an empty field is a different act from a disagreement with an authored one

F4  Six of twelve dimensions are near-constant, and "flat" is ambiguous

Stratified diagnostic, 60 items at 20 per section, 60/60 answered, $0.0048.

DimensionTop level holdsMean confidence
answer_key_correct60/60 (100%)— noul
stem_self_contained60/60 (100%)— noul
tested_construct_is_sat60/60 (100%)— noul
sat_choice_conventions56/60 (93%)0.76
sat_stem_conventions55/60 (92%)0.83
one_defensible_answer37/40 (92%)— noul

A flat dimension has two causes needing opposite responses. Either the bank is genuinely clean on that axis, which is plausible for items hand-authored to a content bar, or the dimension cannot discriminate and is reporting a constant.

A negative control tells them apart, and Phase 0 already ran one without naming it as a method. answer_key_correct looks flat here, yet the paired probe showed it drops to 0.16 when handed a wrong key. It discriminates; the bank is simply correct. The same trick resolves the other five.
  • Run a negative control for each flat dimension before Phase 3 — strip the passage and check stem_self_contained falls, break choice parallelism and check sat_choice_conventions falls. A dimension that does not move under deliberate corruption must not gate.

F5  Engagement is substantially measuring whether the item has a passage

SectionMean engagement
reading1.91
writing1.36
math0.64

It has the widest spread of any dimension (sd 0.62), which looks like discrimination and is mostly the presence of a stimulus. A math item is "bare" by construction, so the dimension splits by section rather than by quality and fails the non-redundancy test against section.

This is evidence for the decomposition meeting rather than a defect to fix here. It also sharpens the meeting's first question: a dimension that scores every math item low is measuring something the item cannot change, which is the actionability test failing in a form that is easy to miss because the numbers look healthy.

F6  The three least certain dimensions include the one designated to gate

DimensionMean confidence
passage_realism0.45
difficulty0.51
distractor_pull0.52
engagement0.63
sat_choice_conventions0.76
sat_stem_conventions0.83
skill_code0.96
section1.00

distractor_pull was chosen as the calibratable neighbour of engagement and is the dimension the rubric allows to gate. It returns the third-lowest confidence in the set. TypeSafe reads low confidence on a Score as levels that are ambiguous, multi-dimensional, or a state carrying too little to decide. All three are fixable in the rubric, and none is fixable by a threshold.

Agreement against the corpus, on the 23 of 60 sampled items that carry labels: section 60/60 (100%), skill_code 20/23 (87%), difficulty 13/23 (57%). The first two look calibratable today. difficulty at 57% on a binary mapping is close to a coin flip, and 23 items is too thin to conclude more.

  • Rewrite distractor_pull's levels before Phase 3 and re-measure its confidence
  • Decide whether difficulty survives as a Jev dimension, or whether key_separation from F2 is the better-grounded route to the same judgement

F7  The negative control answers F4: flat means a clean bank

Every flat dimension was handed a deliberately corrupted item and asked again. 15 items, 87 requests, $0.0019.

DimensionCleanBrokenDropMoved onCorruption
sat_stem_conventions1.890.051.8415/15stem names the strategy, numbers a step
sat_choice_conventions1.840.051.7915/15fifth choice, "All of the above"
stem_self_contained0.940.140.8115/15passage removed
answer_key_correct0.930.450.4811/15key pointed at a distractor
one_defensible_answer0.780.480.3012/12distractor replaced by the correct text
None of them is reporting a constant. F4's worry resolves in the favourable direction: the bank is clean on these axes, and the dimensions can tell when it is not.

The ordering is the useful part. The two convention Scores collapse almost completely under corruption and are the sharpest instruments in the rubric. one_defensible_answer has the weakest drop at 0.30 and only reaches 0.48 when broken, so it detects a second defensible answer without becoming confident about it, which is a thin margin to put a threshold in. answer_key_correct moving on 11 of 15 rather than 15 of 15 is F2 again: the items it misses are the ones whose distractors are well authored.

  • tested_construct_is_sat remains untested — an out-of-domain stem also breaks self-containment, so no corruption yet isolates it. It stays advisory.

F8  The distractor pass reaches 93 of 439 items, and my first trap question was unfair

Only 93 items carry per-option mechanism or trap_codes on their distractors, 45 Wager and 48 Lineup. The other 346 have nothing for this pass to check, which matches the guide's own account that a generated item carries a con nickname and no mechanism by definition.

Question, across 183 distractorsMeanMedianBelow 0.5
Does the authored mechanism explain its option?0.7130.77016%
Does the authored trap_code name the error?0.5850.60039%
The trap figure first measured 0.495 with 58% below 0.5, and that was mostly my question's fault. It sent the bare code — M-ALG-MANIP — and asked whether it named an error, without ever supplying what the code means. TypeSafe's own guidance says symbolic representations underperform semantic ones, and a trap code is exactly that. A paired test supplying the definition from guides/sat-traps lifted the mean by 0.124 and improved 24 of 30 options, so the runner now carries the definition and the numbers above are corrected.

A residual survives the fix. 39% still sit below 0.5, against 16% for the mechanism question on the same options. That gap is the part that may be about the corpus rather than the question.

  • Read the ten weakest trap-code fits by hand and decide whether the residual is a labelling problem or a second framing problem

What is being evaluated

ScopeCountShape
Phase 1 target — the-wager + the-lineup439class: item with a dict options[], per-option trap_codes and mechanism
Deferred — bundled-drills, target-practice, go-undercover, the-statement, the-lobby173answer_options / move_options; each needs its own state builder
Deferred — prime-suspect9Case-shaped, already gated by validation_status
Out of scope — class: flavor215Heckles, con taxonomy, glossary. Not items.

Section split of the 439: reading 178, writing 131, math 130. Two entries (rs-halden, rs-skerry) carry a non-dict options list and are skipped with a named reason rather than silently.

The dimensions, and how each maps onto Jev

DimensionTypeGates?Note
sectionChoiceyesSanity check against the corpus value
skill_codeChoiceyesThe eight College Board codes already in the schema
difficultyScoreyesFour levels described as situations; banded onto the corpus's standard/hard
sat_stem_conventionsScoreyesHalf of "fidelity to the digital SAT", split because one Score cannot hold two judgements
sat_choice_conventionsScoreyesThe other half
tested_construct_is_satNouladvisoryNo confidence on Nouls
stem_self_containedNouladvisoryCatches the failure the guide already names: stems pointing at a stimulus never shown
passage_realismScoreyesReading and writing only
distractor_pullScoreyesThe calibratable proxy for whether a round feels alive
engagementScoreadvisorySee below
one_defensible_answerNouladvisorySuppressed for math, and the suppression is reported
Engagement is marked advisory, and that is a real reduction in what you asked for. It has no ground truth to calibrate a threshold against, and the guide's own bar — "don't gamify the question, gamify the reason you care about the question" — is a property of the mini game's loop, not of an item read in isolation. It still gets scored and reported. It never gates. distractor_pull is the nearby thing that is calibratable, and that one gates.

Verdict routing

Three bands, following TypeSafe's confidence-gated routing pattern. This is the part that keeps Claude off work it cannot do.

ConditionVerdictGoes to
Every gating dimension meets its thresholdpassNothing
A dimension misses, confidence below its floorreviewYou. Jev could not read the item; a revision request built on it would be noise.
A dimension misses, confidence at or above its floorreviseClaude, with the dimension, the score, and the level description it landed on
revision_passes at the capescalateYou, regardless of score

Routing low confidence to a human rather than to Claude is what stops the loop generating work from its own uncertainty.

The evaluation record written onto each entry
"evaluation": {
  "rubric_version": "2026-09-20a",
  "model": "jev-1.13.0",
  "run": "20260920T143012",
  "verdict": "pass",
  "dimensions": {
    "skill_code": {
      "type": "choice", "choice": "algebra", "confidence": 0.81,
      "probabilities": {"algebra": 0.88, "advanced-math": 0.12},
      "corpus_value": "algebra", "agrees": true
    },
    "sat_stem_conventions": {"type": "score", "score": 2.4, "confidence": 0.77},
    "engagement": {"type": "score", "score": 1.9, "confidence": 0.52, "advisory": true}
  },
  "composite": {"sat_fidelity": 0.78, "craft": 0.71},
  "flags": ["skill-mismatch"],
  "revision_passes": 0,
  "evaluated": "2026-09-20"
}

Four architecture decisions, each of which had a real alternative

Phase 0 · Access, client, and an honest probe complete

The probe did its job. It was meant to decide whether a third of the bank was in scope, and it reversed the answer the plan had assumed. It cost half a cent.

Phase 1 · The rubric as corpus data after Phase 0

Phase 2 · The runner, reporting only after Phase 1

Phase 3 · Calibration — the gate on everything downstream needs your labels

This phase exists because the bank has already been burned once by an uncalibrated judgement: the difficulty backfill put hard on 71% of The Wager and had to be retuned the next day. A model score adopted without checking it against your own reading would repeat that at scale.

Phase 4 · Writing verdicts to the corpus after Phase 3

Phase 5 · The bounded revision loop after Phase 4

No direct Anthropic API client. Revision runs through claude -p in headless mode, the same shape CodeRaven's SimpleClaudeRunner already uses, with limits in front of it.

Phase 6 · App surface deferred, not scheduled

Final review confidence 78 / 100 · was 74

What this solves

The bank's confidence score is an authoring self-assessment — the author's own number about their own work. This adds a second, independent read with a calibrated uncertainty signal, at a cost low enough to re-run on every rubric edit. The routing split is the part that earns its place: a confident negative is work for Claude, an uncertain read is work for you, and the two never get confused.

Pros

Cons and what is genuinely uncertain

Where this plan could be inadequate

Adjustments worth considering

Why 78, up from 74. The largest technical unknown resolved favourably: math answer-checking works, measured on the real bank rather than assumed from a vendor warning, and the route and wire format are verified end to end. F3 holds it back, having opened a structural question the plan does not yet answer.

Why 74 was the first number. Three of the five dimensions you named map cleanly onto Jev, and two do not. Difficulty and skill classification are exactly what a calibrated classifier is for. SAT fidelity decomposes well. Realism is workable. Engagement has no ground truth, and math correctness is outside the model's competence by its vendor's own account. A plan that promised all five equally would be more confident and less true. The number moves up after Phase 0's probe and Phase 3's agreement rates, which is why both are gates rather than steps.