Skip to content
Empryo
Engineering note, 2026-09-16

A classifier inside the coding agent

TypeSafe is a separate service. Its model, jev, answers multiple-choice questions: send it a situation and a list of questions, and it returns an answer to each with a probability.

What kind of error is this?

Input exceeds the maximum length of 1048576 characters

  • context overflow0.96
  • worth a retry
  • permanent

One question, a fixed list of answers, a probability.

In short

We tried jev wherever a cheap, fast decision might save an expensive model call, then measured it against our own past sessions. One use clearly beat the code it would replace: deciding what to do when a model call fails. Two others, re-ranking grep results and reading review verdicts, were measured and removed. Behind every number here: 290 jev calls, $0.011 in total.

What we tested
Re-ranking 220 replayed searches, reading 70 real review reports, and sorting 24 real failed-call incidents.
What helped
Error triage on failed model calls: 24 of 24, one more than the patterns it backs up. It ships, off by default.
What we removed
Grep re-ranking (right file in the top 3 fell from 78.6% to 74.1%) and review verdicts (2 false passes in 20).
What we found on the way
A search bug: 55.6% of capped searches came back empty. Fixed, no classifier needed.

What it costs, next to what it replaces

one median Empryo turn (35,787 priced turns)$0.1968
every measurement on this page$0.011, 290 calls
one jev call$0.00005, 244ms median
Drawn to scale against one median turn. One jev call is narrower than a pixel here, so it is shown as a dot. A decision that costs a four-thousandth of a turn can be wrong now and then and still pay for itself, as long as its mistakes are cheap.

One turn, step by step

Steps marked jev use jev. Steps marked model use the model you pay real money for. The rest runs locally.

  1. you send a messagelocal

    From the terminal, the desktop app or headless mode.

  2. the Genome is already builtlocal

    Empryo's map of your code, built in the background rather than on every turn.

  3. the request is put togetherlocal

    Instructions, tools, a summary of the map, and your message.

  4. jev picks a skill, or nonejev

    All 136 installed skills ranked in one call; at most one is suggested. About 1s, $0.0004.

  5. the model reads everythingmodel

    Tens of thousands of tokens at $1 to $10 per million. This is where the money goes.

  6. it searcheslocal

    Find and grep return candidate files, ranked by name match, centrality and git history.

  7. jev re-ranks the find resultsjev

    One call, 244ms median, $0.00005. In shadow mode it shows what it would have done; the model still gets the original order.

  8. it reads and editsmodel

    Every read of the wrong file costs a full round trip at model prices.

  9. repeatlocal

    Steps E to H repeat, and each round costs more than the last.

  10. the itemized billlocal

    Every jev call is added to the tab's cost and to /usage, at $0.042 per million tokens.

Two uses we measured and removed

Re-ranking grep results

Live side-by-side runs told us nothing: the same prompt on the same model took 14 steps, then 6, then 9. So we replayed past sessions instead. We took every grep in 19,828 recorded tool calls, in Empryo’s own order, and used the files the turn went on to read as the answer key. We only scored searches where the answer was in the list, because the 32KB output limit decides that before any ranking happens.

right file first (n=220)

Empryo order89 of 220, 40.5%
jev order97 of 220, 44.1%

right file in the top 3 (n=220)

Empryo order173 of 220, 78.6%
jev order163 of 220, 74.1%

top 3, only files the turn edited (n=97)

Empryo order69 of 97, 71.1%
jev order57 of 97, 58.8%
Share of replayed searches, drawn to scale from 0 to 100%.

21 searches fixed, 31 broken. The bar, set before the first call, was +15 points and three fixes for every break. This went the other way, so the code was written, measured and deleted. The useful finding was elsewhere: in 32.4% of searches the file the turn ended up reading was never in the output at all, which led to the bug fix below.

Reading review verdicts

A review agent’s report ends with VERDICT: PASS | FAIL | PARTIAL. When that line is missing, a model call works out the verdict, and jev looked like a $0.0001 replacement. We tested it on 70 real reports with the line removed, using the reviewer’s own verdict as the answer key.

first phrasing16 of 56, 28.6%
second phrasing12 of 20, 60.0%
Confident answers that matched the reviewer. In the second phrasing, 2 of 20 said PASS where the report said FAIL. A wrong FAIL costs one round of coding; a wrong PASS lets broken code through, so this use was removed too.

A search bug we found along the way

Search output arrives in chunks, and each chunk was decoded as text on its own, so a character split across two chunks turned into garbage. Worse, the chunk that crossed the 32KB limit was thrown away whole instead of being trimmed.

55.6%1,742 of 3,134 capped searches, across 258,802 recorded greps, returned no matches at all. The model saw only “output capped” and treated that as the answer.

Both bugs are fixed: output is collected whole, trimmed to the limit and decoded once. The decoding bug never triggered in our data (0 garbled paths in 258,802 outputs, all ASCII), so the replay numbers above were not skewed by it.

Where it helped: failed model calls

When a model call fails, Empryo decides whether to retry, shrink the conversation or end the turn by matching the provider’s error message against patterns. Those messages change without notice, so the patterns need frequent fixes.

jev, untuned24 of 24, 100.0%
Empryo's patterns23 of 24, 95.8%
Real incidents sorted correctly. The patterns were written from these same 24 messages, so 23 of 24 is their best possible score; jev had never seen any of them.

The one case jev catches and the patterns miss is a Codex error that reads Input exceeds the maximum length of 1048576 characters. It never says “context” or “token”, so recovery never started. jev labels it a context overflow with 0.96 confidence.

This shipped as error triage, off by default. The patterns still decide; jev is asked only when they have called an error permanent and the turn is about to end. It is never asked about sign-in or key errors, answers below 0.7 confidence are ignored, and a session with no failures makes no calls. Median time is 263ms.

The code map rarely lists the files you edit

Empryo gives the model a short, ranked map of your repository, most central files first. Across 87 local sessions, the top 120 entries included only 8.5% of the files those sessions went on to edit; the top 200 reached 13.1%, and only 2 sessions were fully covered.

the map as it was, 87 sessions

top 120 entries8.5%
top 200 entries13.1%

pool test, 30 sessions, 278 edited files

centrality ranking59 of 278, 21.2%
jev80 of 278, 28.8%

the same 120 entries, 43 sessions

central files only10.7%
central plus often changed, shipped40.2%
change frequency alone42.4%
Share of edited files the map covers, drawn to scale from 0 to 100%.

In the pool test each session had 120 candidate files, the ones it edited plus random decoys, and jev saw only the first message. It found 80 of 278 against 59 for centrality, with one 348ms call per session. Better, but under our bar of +15 points and three fixes per break, so it is not shipped.

What did ship needs no classifier. Ranking the map by how often files change as well as how central they are raised coverage from 10.7% to 40.2% in the same 120 entries. Change frequency alone reaches 42.4% but leaves out the core types you need to find your way around. The history comes from git, which the Genome already reads.

Three ideas we dropped before building

Each was ruled out by existing data before we spent a single call.

which lines of a big file to read no headroom
Across 431,003 partial reads, only 1.3% of re-reads went outside lines already read, and 85.2% of 52,565 edits landed inside them. A perfect predictor would gain about one point.
which memories fit this turn not measurable
Session logs record the memories that were used, not the candidates that were passed over, so there is nothing to re-rank until we record them.
which subagent findings reach the parent not attempted
A wrongly dropped finding cannot be recovered and nobody notices. The same reason rules out choosing what to keep when a conversation is summarized.

What ships today

wherestateevidence
find / symbol searchshipped, off by default, shadow moderight file in the top 5 went from 59% to 73% on 80 prompts, one point under our bar
skill suggestionshipped, off by defaultno measured gain yet
error triage on a failed callshipped, off by default24/24 against the patterns' 23/24 on real incidents
grepnot shippedright file in the top 3 went from 78.6% to 74.1% on 220 replays
review verdictsnot shipped60% agreement, 2 false passes in 20
prefetching files for the modelnot shippedright file first 35% vs 32%, but two of every three prefetched files were wrong
which lines to readno room to improve85.2% of edits already land in lines the turn read
memory recallnot measurable yetlogs record the memories used, not the candidates
ranking the map in the promptmeasured, not shipped28.8% vs 21.2% for centrality ranking: a real gain, but under our bar
the 32KB search limitbug fixed55.6% of capped searches returned nothing. No classifier involved
summarizing long conversationsruled outa wrongly dropped detail cannot be recovered, and there is nothing to compare against
choosing tools ahead of timeruled outchanging the tool list between turns would make every turn cost more

Try it yourself

TypeSafe is off until you add a TypeSafe API key and turn it on. In the terminal app, run /typesafe; in the desktop app, open the TypeSafe settings. Each call appears in the chat with what it decided and what it cost, and later in /usage. Setup is in the docs.

Behind this page: 290 jev calls, $0.011 total, 244ms median and 283ms at p90. Live runs used Claude Haiku 4.5; the replayed reviews were written by Opus 5.

Sharehttps://empryo.com/typesafe?theme=undertow&appearance=dark