Engineering

Jev gets a vote, not a veto

Trinqa asks Jev — TypeSafe’s System One decision model — which Stellar payout route looks better, and then ignores it whenever it is slow, unsure, or being asked to do arithmetic. Here is the whole arrangement: the three layers, the weights, and every way the model gets overruled.

Trinqa engineering 20 September 2026 9 min read
Jev gets a vote, not a veto — five of seven scoring factors are ones the model may advise on, while cost and speed are computed in code.

When you move money out of Trinqa to a local bank account, something has to choose how. On Stellar that means picking an anchor — the business that takes stablecoin on one side and pays out local currency on the other — and a rail inside it. Several may be able to serve the same withdrawal, and they differ in ways that matter: published fees, minimum and maximum amounts, estimated settlement time, whether the anchor is answering health checks at all, and whether your identity checks have cleared there.

Some of that is arithmetic. Some of it is judgment. Our position is that those two should never be done by the same thing.

Why not just ask an LLM

The obvious move is to hand the candidate list to a general model and ask which is best. We did not want that, for three reasons.

A chat model answers in prose, so you have to parse the answer, and parsing can fail at the exact moment money is moving. It takes seconds when the user is watching a spinner. And most importantly, it makes a text generator the thing that decides where funds go, with no natural boundary around how much of that decision it owns. The interesting question was never "can a model pick a route" — it is how little of the decision a model needs in order to be useful.

What Jev is

Jev is TypeSafe's first "System One" model. It does not generate text. You give it a state and a set of named, typed questions, and it returns typed answers with calibrated probabilities and a confidence score. There are three question types: choice (pick one of these options), score (place this on a rubric), and noul (is this statement true, 0–1). Every question is evaluated in parallel against the same state in one call.

Because the answer is constrained to the shape you declared, a malformed or invented answer is not something you have to defend against — the model cannot return one. TypeSafe reports end-to-end responses in the 70–500 ms range, with input priced at $0.042 per million tokens and output unmetered. An independent write-up puts typical calls near 100 ms and makes the point we agree with: it is a component inside ordinary application code, not a thing that runs the workflow.

The shape of it

A traditional model gives you a paragraph you must interpret. Jev gives you a number you can compare, plus a confidence you can threshold on. That difference is what makes it safe to put near a payment — not that it is smarter, but that its output has edges.

Three layers, in order

Our route decision has exactly three stages, and the model appears in only one of them.

Where Jev sits in a payout decision

Candidates in on the left, one chosen route out on the right. Only the middle stage involves a model — and the decision completes whether or not that stage answers.

1

Deterministic filter

Drops routes that cannot serve this request — wrong currency, below the minimum, above the maximum, identity checks not cleared.

2

Jev advises

Scores five judgment factors on a rubric we wrote, each answer carrying its own confidence.

3

Deterministic score

Our weights, our arithmetic. Ranks what survived and picks the winner.

Rejected routes keep their reason and are shown as rejected, not hidden.
Stage 2 can be skipped entirely — stage 3 still produces a route.
The model is a stage that may be missing, not a dependency the pipeline is built around.

1. A deterministic filter

Before anything is judged, routes that simply cannot serve the request are dropped, each with a recorded reason: the currency is not supported, withdrawals are not supported, the amount is below the minimum or above the maximum, the anchor is missing the connection we need, it is unavailable, identity checks have not cleared, or it is not in a state that can actually execute. These are facts about the request and the anchor's own published metadata. No judgment is involved, so no model is involved.

The reasons survive into the app. A route that was dropped is shown as dropped, with the clause that caused it — "caps payouts below this amount", "needs identity checks to be completed first" — rather than quietly disappearing from a list.

2. Jev advises — on five factors out of seven

Every surviving candidate is scored on seven factors, each 0–100, combined with fixed weights that sum to 100. Jev is allowed to influence five of them. It is never asked about the other two.

The seven factors, and who produces each number

Weights are fixed, sum to 100, and live in our code. Jev may influence five of them — never the two that are arithmetic.

Cost
35
Speed
15
Reliability
15
Room under the limit
10
Fits your timing
10
Fits your risk setting
10
Effect on your earnings
5
Arithmetic, in our code — 50 points Jev may advise — 50 points
Show as a table
FactorWeightWho produces it
Cost35Arithmetic, in our code
Speed15Arithmetic, in our code
Reliability15Jev may advise
Room under the limit10Jev may advise
Fits your timing10Jev may advise
Fits your risk setting10Jev may advise
Effect on your earnings5Jev may advise
Cost and speed carry half the total weight between them, and neither is ever sent to the model.

Cost and speed are excluded on purpose. Cost is a fee percentage plus a fixed fee plus any spread in the quote, divided by the amount; speed is settlement minutes. That is arithmetic on published numbers, and TypeSafe's own guidance on Jev's rough edges is blunt about it: keep the arithmetic in code. The same page notes that this model's score levels are weak in numerical calibration and should not be used to interpolate exact values. So the two heaviest factors, carrying half the weight between them, are computed from fee schedules and estimated minutes and never leave our code.

What is left is the part that is genuinely judgment — how much to trust an anchor's health signal, whether the amount sits comfortably inside the limits, whether this route's pace suits someone who needs the money in four days rather than four weeks. Each is sent as a score question against a five-level rubric we wrote, lowest first:

"r0_liquidity": {
  "type": "score",
  "instructions": "Judge how comfortably the requested amount fits within
                   this route's published rail limits (min/max), given in
                   `state`. Do not perform the arithmetic comparison
                   precisely yourself; give a qualitative judgment of
                   headroom.",
  "criteria": [
    "Requested amount is far outside the published min/max limits.",
    "Requested amount is close to a limit boundary, little headroom.",
    "Fits within limits, but headroom is unclear or only moderate.",
    "Comfortably fits within limits with good headroom.",
    "Fits with ample headroom, well within both min and max."
  ]
}

Note what the instruction says: do not do the comparison precisely, give a qualitative judgment. We are asking for the thing it is good at and explicitly waving it off the thing it is not. The answer comes back as a score on that rubric plus a confidence, and we normalise it to 0–100 using the legend the model returns when it has one, falling back to the rubric length we sent. When more than one route survives the filter we also ask a single choice question — which route would you recommend, on these judgment factors only — and use the answer for nothing but a line of rationale attached to the winner.

The state is deliberately thin — the request, and for each route only its name, status, estimated minutes, reliability signal, fee fields, and rail limits. TypeSafe documents that accuracy falls as unrelated detail accumulates in the state, because it acts as a distractor. So nothing goes in that a question does not need.

3. Deterministic scoring, with our weights

Whatever comes back, the final score is Σ (weight × factor) / 100, using weights that live in our code and are not in the request, not in the response, and not negotiable by the model. Jev can move five inputs. It cannot move the formula, and it cannot move what those inputs are worth.

Every way the model gets overruled

The interesting part of this design is not the call. It is everything around the call.

What one answer has to survive to count

Four gates, applied in order. Failing any of them costs that answer its influence — never the decision.

Did it arrive in time?

2.5s in the adapter, 3s on the whole call. A late answer is simply not there.

Did it parse?

Unknown or missing fields drop that route’s advice rather than throwing.

Is confidence ≥ 0.6?

Checked per factor, so one uncertain judgment does not discard the confident ones beside it.

Then it is used — and labelled

The factor is marked advisor; everything else stays deterministic.

timeoutlow_confidenceinvalid_outputerrordisabledno_routes

Whichever gate closed, the decision records why in one of those six names, and scoring carries on without the advice.

None of these gates can produce a worse outcome than not calling the model at all.

Turn Jev off entirely and Trinqa still picks a route. That is the test we held the design to: the model is an improvement to a system that works without it, not a dependency the system is built around.

What we are not claiming

Honest limits

The general shape

Strip out the anchors and the stablecoins and there is a pattern here that has nothing to do with payments.

Decide first which parts of a decision are arithmetic and which are judgment. Compute the arithmetic. For the judgment, ask a model questions with edges — a rubric, an enumerated choice, a boolean — instead of asking for an opinion in prose. Take a confidence score with each answer and set a threshold you are willing to defend. Keep the weights. Put a timeout on the whole thing. Record why you ignored it when you did. Then label, in whatever you show the user, which numbers a model touched.

None of that requires trusting the model. That is the point: we would rather build something whose correctness does not depend on the model being right, and then let a model make it better.

Trinqa is in early access

Money that keeps working until you need it. Join the waitlist and we will reach out when your spot is ready.

Sources