
That is a decision model in two sentences, from Aaron Francis replying to James McDonald on X. The rest of this article is the same distinction with the thresholds, the file names and the numbers attached.
A decision model answers a closed question with a calibrated probability and cannot write a sentence. Routines v1.6.0 moves four chat model verdicts onto one, Jev, and a fifth, the meeting folder pick, moved earlier in v1.5.15. Every threshold that acts on those probabilities is named below, and the whole live proof behind the numbers cost $0.000102.
Routines asks a chat model to do the writing. Four times we also asked it for a verdict: continue or stop, pass or fail, keep or drop, file here or nowhere. A fifth verdict, whether a finished output is worth a badge, was never a model call at all, only a keyword test. All five now go to a decision model that returns a calibrated probability and cannot write a sentence: four of them in v1.6.0, out now, and the folder pick earlier, in v1.5.15. The model is Jev, and it is TypeSafe AI's, not ours.
The five places we needed a verdict
- Flow edge gate: does this flow continue down this edge.
- Output double check: does this output meet its goal.
- Meeting to-do owner: is this action item yours or theirs.
- Meeting folder pick: which folder in your notes do these meeting notes go in.
- Needs-attention badge: is this output worth a badge.
The first four are chat model calls shaped for parsing: tool-less, history-free, a JSON schema on the request, a salvage parser on the response. The gate and the double check run at the fast tier at temperature: Some(0.0), the to-do owner at 0.1, the folder pick a tier up at 0.2. That folder pick was the first to move: with a key stored and at least two candidate folders it answers from Jev before the chat request is built, and it shipped earlier, in v1.5.15. The fifth was never a model call. The badge is a keyword test, re-run on every React render, checking the text for "high-priority", "high priority" or "urgent".
You are an impartial reviewer. You are given the GOAL of an automated task and
the OUTPUT it produced. Judge ONLY whether the output fulfills the goal and is
internally consistent (dates, numbers, and claims do not contradict each other).
Respond with ONLY a JSON object of the exact shape {"pass": boolean, "reason":
string, "fix": string} where "reason" is one short sentence and "fix" is a
concrete instruction for what to change (empty string when pass is true). No
prose, no markdown.
The gate's older prompt told the model to answer continue when it could not tell, and so laundered every uncertain run into a clean green pass. We fixed it, and the doc comment now states the rule it keeps: "a gate that broke is never recorded as a clean verdict." The same comment names the site that still launders: "verify_output absorbs an unreadable grader result into a plain passed; flows must not." That one is live, deliberate and pinned by a test, because an unreadable verdict must never fail the run.
What a System One decision model is
Jev is TypeSafe AI's System One model, and the launch post is theirs to explain. The wire contract is a POST to https://api.typesafe.ai/v1/systemone under a Bearer key, carrying state, model and a map of questions whose keys are yours. A noul answers yes or no with one probability between 0 and 1. A choice picks one of a map of named options, with a probability for each and a confidence. A score places it on an ordered rubric of at least two levels and returns a score, a distribution, a legend and a confidence.
The needs_attention half of the badge's request, with the two criteria strings elided and the state stood in for. A how_soon score rides in the same call.
{
"state": {
"routine_name": "<the routine's name>",
"output_excerpt": "<the first 800 characters of the output>"
},
"model": "jev-latest",
"questions": {
"needs_attention": {
"type": "noul",
"instructions": "Does this output tell the user about something of their own that needs action soon?",
"criteria": {
"true": "[...]",
"false": "[...] names a company or product whose name contains such a word"
}
}
}
}
This came back on a decoy we wrote to break the keyword test, usage and legend elided.
{
"model": "jev-1.13.0",
"answers": {
"needs_attention": { "type": "noul", "noul": 0.03 },
"how_soon": {
"type": "score",
"score": 0.08,
"probabilities": { "0": 0.92, "1": 0.08, "2": 0.0 },
"confidence": 0.88
}
}
}
A noul answer has no confidence field. Its only fields are its type and its probability, and the probability is the uncertainty. Jev cannot write text at all, so it replaces the parse and leaves the prose to the chat model. Laravel's AI SDK shipped the same shape on 2026-09-17 in PR laravel/ai#1010, as Boolean, Choice and Score, provider id typesafe, default model jev-latest. We mirrored its names.
The architecture: LLM generates, Jev decides, code controls
The LLM still writes. Jev returns a number. Code owns the threshold, a named constant you can read. When the number falls short, the answer routes somewhere that already existed: the chat path, or a person.
Figure 1. LLM generates, Jev decides, code controls, and the uncertain band routes to a path that was always there.
Figure 2. One decision, end to end. With no key stored, the run takes the path it always took.
How we chose where to put it
Four blind finders swept the codebase by pattern, by subsystem, by data and by cost. We merged their candidates and measured volume against the app's own SQLite database. The formula is expected win times measured volume, divided by risk, both on a 1 to 5 scale, where risk is how badly and how silently a wrong answer lands, times how much surface the change touches.
Figure 3. The inventory funnel. The per finder counts were never recorded, so the figure names the four angles and counts only what the review doc actually holds.
The raw count before dedupe was not recorded either, so we put no number on it. What survived: 18 ranked candidates, 4 implement now, 8 later, 6 never.
The two candidates the brief itself seeded, the gate and the double check, both measured zero current volume: zero of 6 flow edges carry a condition, zero of 687 outputs carry a verify status. They shipped as ranks 1 and 2 anyway, each on its own stated forward assumption. The gate's: flow runs ran 2 per day from 2026-08-08 to 2026-09-03 and then stopped, so 2 runs a day on 1 gated edge is 2 evaluations a day. Grounded in history, but an assumption, and the review labels it as one.
On measured volume the order differs: 6.9 for the badge, 3.4 for the to-do owner, 5.0 for the gate on its assumed volume, 0.78 or 17.0 for the grader, both of those on assumed volume as well. The highest measured volume of the four, 10.27 completed routine outputs a day, belonged to the badge, which neither the brief nor half the finders led with, because it was a substring test and not a model call.
The four sites we shipped
Each has a named constant, a route for an uncertain answer, and the same no-key guarantee: the typed path does not exist and the old branch runs.
Flow edge gate. GATE_JEV_CONTINUE_MIN = 0.85. At or above it the flow continues and spends no chat call; below it, the evaluation escalates to the chat gate. Jev may never stop a flow.
Output double check. VERIFY_JEV_PASS_MIN = 0.85 on the score's normalized() value, plus VERIFY_JEV_CONFIDENCE_MIN = 0.6 when a confidence is measured. From VERIFY_JEV_CONCERN_MAX = 0.5 to 0.85 it escalates to the prose grader. Below 0.5 it escalates too, and marks a concern: that is the one band where an unreadable grader verdict is no longer absorbed into a pass.
Meeting to-do owner. TODO_OWNER_USER_MIN = 0.75 keeps the item, TODO_OWNER_DROP_MAX = 0.25 drops it, and between them it falls through to today's string verdict. The keep line sits below 0.85 on purpose: a wrong to-do dies in one gesture, a dropped one is a commitment nobody sees again.
Needs-attention badge. ATTENTION_FLAG_MIN = 0.85 raises the badge. Everything else answers nothing, the column stays NULL, and the keyword test runs as before.
Figure 4. The gate before and after. Only a confident yes skips the chat call.
Figure 5. Every band, every constant. Nothing acts at or below 0.5, because on a two option question chance alone puts 0.5 on the winner.
The fifth band is that meeting folder pick, MIN_FOLDER_PICK_CONFIDENCE = 0.6, which shipped in v1.5.15. The four above it are what v1.6.0 added.
Tests pin the no-key guarantee. The decide tool registers only when the route finder locates a key, and the badge's pin asserts nothing was sent, so the stop happens before a request is built. This one pins the behaviour we criticised above.
#[tokio::test]
async fn with_no_key_an_unreadable_verdict_is_still_absorbed_into_passed() {
// THE pin. Today an unreadable grader verdict delivers the run marked
// "passed" with no note, and with no decision key that must not change
// by a single byte.
let (result, sends) = run_double_check(
JevVerdict::Unjudged,
&verify_long_output(),
vec![Ok("totally not json".to_string())],
)
.await;
let result = result.expect("a verdict was returned");
assert_eq!(result.status, "passed");
assert_eq!(result.content, verify_long_output());
assert_eq!(result.note, None);
assert_eq!(sends, 1);
}
The live numbers
The live proof is four ignored probe tests, one per site. The badge's probe loops over two cases, so four probes make five calls: $0.000016, $0.000019, $0.000028, $0.000020 and $0.000019, totalling $0.000102. Our own report said $0.000083, which is the first four of those five: it counted probes, not calls.
Figure 6. Cost per call, re-derived from the token counts at $0.042 per million input tokens. Latency is not charted: nothing in this codebase times a model call.
At $0.042 per million input tokens with output free, the gate's estimated 3,250 tokens cost $0.000137 against $0.0034 for the chat verdict, 25 times cheaper; the grader's 3,560 tokens cost $0.000150 against $0.0040, 27 times. Those token counts are the finder's estimates, not measured request bodies, and the chat side is priced on the Haiku 4.5 fallback basis: this Mac runs a tier with no price row, so the app stores NULL, never a fabricated zero. At this Mac's measured volume for both sites, zero, the saving per day is exactly $0.00. Jev's published latency is 70 to 500 ms, the vendor's number and not ours.
Figure 7. The pair that made the case. The shipped keyword test is wrong in both directions; Jev is right in both.
The pair that made the badge's case ran against jev-1.13.0. The decoy: "UrgentIQ raised a 15M Series A this week. Analysts call the compliance gap it targets large, urgent and under-served." The keyword test badges it. Jev returns needs_attention 0.03 and raises nothing, because the question's own false criterion covers it: an output that "names a company or product whose name contains such a word" is not one that needs you. The second: "The landlord replied and needs your signature on the lease addendum before Friday or the renewal lapses." The keyword test says FALSE; Jev returns 0.97.
Figure 8. Both outputs through both paths, the substring test and the judge.
We got one precision wrong in review and caught it before it became a ticket. The same call also asks a how_soon score, and on the decoy that came back at 0.08 with 92 per cent of its mass on "no action is needed" and a confidence of 0.88. The 0.88 belongs to the score, never to the 0.03. A noul carries no confidence field. The score is logged, never acted on: output tokens are free, and a later retune of ATTENTION_FLAG_MIN will want this Mac's own distribution.
The laws we banked
- Nothing acts at or below 0.5 on a two option question: chance alone puts 0.5 on the winner.
- An "unsure" option inverts calibration: it makes uncertainty an answer and takes mass from the real outcomes, and with three options chance sits at 0.33.
- Options come from code, never from the state. A choice's winning option name is logged, which makes this load bearing.
- Never gate a tool-enabled agent behind a classifier.
- A
noulhas no confidence field. The probability is the uncertainty. - Log the decision, never the state.
- Every uncertain answer needs a written route, and a transport error takes that route too.
- Keep arithmetic and hard rules in code. Jev judges meaning.
What we did not do, and what is next
Six candidates were marked never. The meeting chat intent router stays a router: law 4 is quoted from our own project memory, and failing open makes it a gate that fails safe, not a gate that stopped being one. The Slack access allowlist stays a hard rule, because a probabilistic access control is a defect by construction. Device sync conflict auto-merge stays manual: a wrong merge is silent, irreversible loss inside the user's own vault, and the conflict count is zero, so there is no calibration data either. Clipboard classifiers are out, because clipboards carry passwords and tokens. The hard rule matchers compare exact internally generated strings, several in hot loops where a network call is disqualifying. The managed wallet proxy is off limits by the brief's own rule.
Three things are next. The confident negative: the badge's judge never returns a confident no today, so one falls through to the keyword test and the bad badge shows. The decoy produced the calibration datum that was missing, and the column and the component already handle a false. We left it out of this pass, because changing decision semantics after an adversarial approval without a re-verify is how unverified behaviour ships. Thresholds tuned on our own data: every constant above is a judgment call, and the user rating column is NULL on every output row, so there is no labelled corpus yet. A pinned model version: jev-latest tracks stable and runs today; once tuned, the pin moves to jev-1.13.0, so a vendor update cannot move a threshold underneath us.
The routines decide verb and the decide tool shipped in v1.5.15, and the CLI reference documents the verb, including the TypeSafe or OpenRouter key it requires. The four sites above are live in v1.6.0. The TypeSafe key card is in Settings under Connections. An OpenRouter key sits on the AI provider settings, where it already is if you use one for chat. Either way the key lives in the macOS Keychain and never syncs between Macs. Without one, nothing about the app changes. With one, the text Jev judges is sent to that provider, and the decision log keeps the verdict, not the words: never the state text, no excerpt, nothing derived from it, with a test that fails if it does.
Routines is a free download for macOS, with a CLI and its command reference. One of the decisions above is visible from the outside: the needs-attention badge, on any routine you build from the inbox triage recipe.