Fan Out Jev Judgments, Compose Policy in Code

Jev API questions belong in one call: keyed Nouls, Choices and Scores return a decision vector. Compose policy in code, validate IDs, retire yes/no subagents.

Jev API questions fan out from one state into four keyed questions in a single call, return an answer vector keyed by the same IDs, and feed a policy written in code alongside a code-only path check
Batch the questions; don't spawn a coordinator for yes/no. One state goes in once, keyed answers come back, and the verdict is written in code.

A reviewer subagent spawns, reads the diff, the rules file and the last few turns, reasons for a few thousand tokens, and ends its paragraph with the word safe. A regex finds the word. Then a second subagent spawns to decide whether the same call matches the task. Two cold sessions and two parsed essays have bought two answers a person gives at a glance.

TypeSafe’s Jev turns that pattern inside out. Its API takes one state and a map of questions keyed by IDs you choose, and answers all your Jev API questions against that state in a single call. By Tuesday you can replace the label-returning subagents in your fleet with one multi-question call per decision, a decision vector keyed by your IDs, and a policy function in code that composes the vector with the checks code does better. You will also have evidence: code, one Jev call and the subagent it replaces, compared on the same labeled checks, with every disagreement logged.

Chatbots suggest; agents act, and every act in a fleet sits behind small decisions like these. Multiplied across every tool call on every lane, how you ask them becomes a cost line and an audit problem.

Sep 15–20: Jev answers a map of keyed questions against one state

TypeSafe launched Jev on Sep 15, 2026, and its API reference shows the shape this piece depends on. A request carries a state (a string, a JSON object or an array), a model, and questions, a map whose keys you pick. The response carries answers, an object keyed by the same IDs rather than an array, plus the model that answered and token usage. The key is never sent to the model; it exists for your code.

Each question is one of three types. A Noul returns the probability that a statement is true and carries no confidence value. A Choice picks one of up to 255 options and returns probabilities plus a confidence. A Score grades on an ordered scale of 2 to 10 levels, can land between levels, and also carries a confidence.

The models page says Jev ingests the state once and evaluates every question against it in parallel, under two budgets that apply at once: 64K tokens for the state plus all questions, and 32K for the state plus the longest question. Input costs $0.042 per million tokens; output is free.

The launch post reports 70 to 500 ms end to end, measured from TypeSafe’s own West Coast base: a vendor range, not an SLA. The speculative fan-out pattern goes further than permitting batches. It recommends putting every question your system needs into one request and letting code decide afterwards which answers matter.

TypeSafe’s Speculative fan-out docs page recommending that all of a system’s questions go in a single request, with code deciding what is relevant after the fact, above a support-ticket triage example Screenshot: TypeSafe docs, “Speculative fan-out - TypeSafe AI” (undated docs page), captured Sep 21, 2026.

The primitives page states the rule this runbook is built on: “One question’s answer is not hidden context for another.” When a judgment depends on several factors, ask about each separately and combine the answers in your own logic.

Practitioners pushed volume through it within a day. Ryan Vogel reported running Jev over 1,500 of his own emails on Sep 16, and a triage demo shown in Theo Browne’s Sep 20 video reportedly averaged about 200 ms per email, roughly 38 a second.

A yes/no subagent buys a whole session to return one bit

A subagent is the right tool when the delegated work needs reading, planning or writing. When not to use a coordinator covers whether to delegate at all, and subagent fan-out metering covers the bill once a tree exists. Neither addresses the narrow case where the delegated work is a label.

That case is common because it is easy to write: “spawn a reviewer to check whether this is safe” is one line in a prompt. What it costs is a cold context per spawn, prose nobody reads, a parser that breaks when the prose changes shape, and one more agent with tools of its own in your tree. A multi-question call returns typed values under names your code already uses, and cannot run a command.

Step 1: Find the subagents that only return a label

Pull a week of spawn logs and list every subagent whose useful output is a yes/no, a label or a level. Write down the questions each one actually answers, one per line. A “safety review” usually turns out to be four or five questions stapled together.

Then give each question an owner. Anything code can answer exactly stays in code: paths, counts, dates, allowlists. Anything that needs synthesis stays with a model that writes. Only checks a person answers in about ten seconds from the state alone are candidates for the vector; the decision-seat tests do that sorting, and this piece does not repeat them.

Check the reviewer subagent answers today Owner after the split Why
Target path is inside this repo code path resolution is exact; never ask Jev
Diff touches more than 20 files code counting belongs in code
Tool class: read, write, network, destructive Jev Choice kind, after a code allowlist literal criteria, one label
Call is safe to run as described Jev Noul safe a glance-sized judgment
Blast radius if the task line is wrong Jev Score risk an ordered scale
Call matches the stated task Jev Noul on_task a glance-sized judgment
The change is correct and ready to merge a reviewer model or a human not a ten-second question

The last row matters as much as the others. A vector replaces the checks that were never worth a session; it does not replace review.

Step 2: Write the Jev API questions as one keyed map with stable IDs

The question IDs are a contract between the request and the policy, so treat them like column names. Pick short, stable IDs (safe, on_task, risk, kind), version the whole map (tool-gate@3), and bump the version whenever an instruction, criterion or option changes. A policy line that reads answers["safe"] should mean the same question next month.

Build the state from the fields the questions need and nothing else. TypeSafe’s jaggedness page for jev-1.13 warns that accuracy falls as unrelated content grows, and says to filter in code and send only the fields each question needs. A tool name, its arguments, the task line and a diff summary usually suffice. A transcript is not a state.

{
  "model": "jev-1.13.0",
  "state": {
    "task": "Rename the invoice export flag and update its tests",
    "tool": "Bash",
    "command": "sed -i 's/exportV1/exportLegacy/g' src/billing/export.ts",
    "diff_summary": "1 file, 6 lines changed"
  },
  "questions": {
    "safe":    { "type": "noul", "instructions": "The command only reads or edits files in the working tree. It does not delete data, rewrite git history, change permissions or send data off the machine." },
    "on_task": { "type": "noul", "instructions": "The command does what the task line asks and nothing else." },
    "risk":    { "type": "score", "instructions": "How much damage would this command do if the task line were wrong?",
                 "criteria": ["0: none", "1: local and reversible", "2: shared state or other users", "3: irreversible or external"] },
    "kind":    { "type": "choice", "instructions": "Which class of tool action is this?",
                 "criteria": { "read": "Only reads.", "write": "Edits files in the repo.", "network": "Sends or fetches over the network.",
                               "destructive": "Deletes, force-pushes or drops data.", "unclear": "None of the above fits cleanly." } }
  }
}

The shape is illustrative; check field names against the API reference and your endpoint’s dialect.

Three details are deliberate. The Choice has an unclear option because Jev has no abstain answer. The Score levels are literal, because jev-1.13 reads criteria at face value. And the path check is missing on purpose: it is not a question.

Send a versioned model ID, not an alias; the TypeSafe SDK constants default to jev-latest unless you set one. Step 7 covers why the pin belongs in every record.

Step 3: Compose the policy in code, and make every gap a no

With the answers back, the policy is a function. The core rule: allow only if the target path is inside this repo (checked in code, never asked of Jev) AND safe meets the threshold from your per-class gate table AND risk is at most 1. The other branches apply the same idea to the rest of the vector.

# tool_gate.py: illustrative policy over one Jev decision vector.
# Threshold values are placeholders; take yours from the per-class gate table.
POLICY = {"qset": "tool-gate@3", "version": "tool-gate-policy@5",
          "safe_min": 0.90, "on_task_min": 0.80, "risk_max": 1.0, "conf_floor": 0.50}

def decide(call, vec, repo_root):
    if not path_inside(call.target_path, repo_root):      # code, never Jev
        return "deny", "path outside repo"
    if not vec.valid:                                     # step 5 failed
        return "ask", "invalid vector: " + vec.error
    kind = vec["kind"]
    if kind.choice in ("destructive", "unclear") or kind.confidence < POLICY["conf_floor"]:
        return "ask", "class needs a person"
    if vec["safe"].noul < POLICY["safe_min"]:
        return "ask", "safe below threshold"
    risk = vec["risk"]
    if risk.score > POLICY["risk_max"] or risk.confidence < POLICY["conf_floor"]:
        return "ask", "risk above 1 or uncertain"
    if vec["on_task"].noul < POLICY["on_task_min"]:
        return "ask", "off task"
    return "no_veto", "all checks passed"

Four rules keep the function honest.

The best outcome is no veto, never an approval. In a harness hook the gate only denies or asks; its pass hands the call to your permission rules and sandbox, which still apply, and a destructive call reaches a person whatever the vector says. The gate itself can fail. If the Jev call times out, returns 429 or comes back malformed, the policy returns ask for writes and deny for destructive classes; if the hook process itself crashes or times out, Claude Code lets the call continue through the normal permission flow, so that wall has to hold on its own. Designing each gate’s fail mode is the rate-limit piece.

Thresholds live per question and per primitive. On the jaggedness page, one question asked as a Noul returned 0.22 while a yes/no Choice put 0.01 on yes, so a Noul threshold does not transfer to a Choice. Keep every threshold in one versioned policy file.

Never do arithmetic across answers. TypeSafe’s own example of a question and its negation, asked as two Nouls, summed to 1.19. Don’t derive one answer from another, and keep counting and date ordering in code.

A missing answer is a no. Every branch that cannot find its input ends in ask or deny. A default of zero or false is how a gate quietly becomes an approval.

Step 4: Enforce both budgets before sending, and set your own question cap

TypeSafe documents two limits that bind at once: the state plus every question must fit in 64K tokens, and the state plus the longest question in 32K. Check both in code, with your own token estimate and a margin, before the request leaves. Gateways list only the 32K figure, so don’t assume the 64K aggregate holds on every door.

When a vector goes over, choose deliberately:

  1. Shrink the state first. Filtering fields in code is free and usually helps accuracy.
  2. Split the map into two requests on the same state if it still won’t fit. You pay for the state twice; log both halves under one decision ID and validate them as one vector.
  3. Never truncate the state to make it fit. A cut-off diff summary is a different question under the same ID.

Handle 422 as a failed gate. TypeSafe describes 422 as a validation failure, such as a missing field or a malformed question, and names the offending field in the body. Its docs do not say an oversized request returns 422, which is why the budget check lives on your side. Log the field, return ask or deny, fix the map.

No documented cap on question count is not the same as unlimited. The budgets cap it; the rate limits (250,000 tokens a second, 1,200 requests a minute) mean a fat batch trades requests for tokens; and each question is a threshold someone must own. Set a ceiling per vector, and require every question ID to appear in at least one policy line. An unneeded question is nearly free in tokens, as the primitives page says, but not in review time.

Step 5: Validate the vector by question ID before any policy line reads it

Validation sits between the response and the policy, and it fails the whole vector on any single problem:

  • Every ID you sent is present in answers, and nothing is there that you did not send.
  • Each answer’s type matches its question.
  • A Noul is between 0 and 1. A Choice names one of your options. A Score falls inside your level range, allowing values between levels.
  • Choice and Score answers carry confidence; Noul answers do not, so no policy line reads one.
  • The response model is recorded, along with the request ID from the x-typesafe-request-id header that both TypeSafe SDKs expose.

Normalize at the adapter, never in the policy. Vercel’s evaluation API calls a Noul boolean with a probability field, and through the AI SDK the Choice and Score confidence arrives under providerMetadata.typesafe.confidence. The endpoint-dialect conformance test maps every door to one shape, and the policy only ever sees that shape.

OpenRouter’s cookbook for gating agent tool calls with Jev takes the same stance. Its client throws on a non-2xx response, a missing answer or a probability outside 0 to 1, “so a broken check never turns into an approval or a review.” It approves only when every check clears 0.9, blocks when any sits at 0.1 or below, and sends the rest to a human.

Diagram of a Jev decision vector: state and a versioned question map pass a code-side budget check, go out as one Jev call, are validated by question ID, and meet code-only checks in a policy function; errors and invalid answers end in ask or deny, and two rulers show the 64K and 32K budgets One call, one vector, one policy in code. Every failure path ends in ask or deny, with your permission rules and sandbox still behind the gate.

Step 6: Run code, one Jev call and the subagent over the same labeled checks

Retiring a subagent is a claim, so measure it. Take one labeled set of checks from your own logs: calls a person later approved, calls that were denied or reverted, and every destructive call you have. The pilot set behind your gate table works; so do 200 checks pulled fresh.

Run three arms over the same set:

  1. Deterministic code, for every question code can answer. Where it applies, it is the baseline to beat, and it usually wins.
  2. One Jev call per check, with the full question map and the step 3 policy.
  3. The subagent as it runs today, with the same prompt, model and parser.

Record cost per check, wall-clock latency at p50 and p95 with retries included, and agreement for each arm. Price the Jev arm from input tokens at $0.042 per million, with the state counted once per call; illustratively, a 2,000-token state plus five short questions comes to about a hundredth of a cent. Price the subagent from your usage export, counting every retry and re-spawn. Measure agreement per question and per decision, against the labels and between arms.

Arm Cost per check Latency p50 / p95 Agreement with labels Disagreements with other arms
Deterministic code measure measure per question it covers list every case
One Jev call measure measure per question and per decision list every case
Subagent it replaces measure measure per decision list every case

TypeSafe’s parallel-questions cookbook is a sanity check for the shape of the Jev arm, not a result for yours: 13 questions about a ~54,000-character article on jev-1.12, five runs each way. One call carrying all 13 cost $0.000497 and took 0.27 s. Thirteen single-question calls cost $0.006090 and took 2.71 s, a time the cookbook says assumes one call after another; concurrency narrows the time gap but not the token bill. Choices, scores and six of the eight Nouls came back identical across the repeats either way.

Horizontal bar chart from TypeSafe’s parallel-questions cookbook: one call with 13 questions cost $0.000497 and took 0.27 seconds, while 13 one-question calls cost $0.006090 and took 2.71 seconds summed sequentially Vendor-run figures on jev-1.12. The state was sent once in the batched call and 13 times in the other; your subagent arm is yours to measure.

TypeSafe’s parallel-questions cookbook showing that batching does not change the answers, above the section “The only difference: cost and speed” Screenshot: TypeSafe docs, “Parallel questions - TypeSafe AI” (undated cookbook, jev-1.12 run), captured Sep 21, 2026.

Retire the subagent for a check class only when the Jev arm matches or beats its agreement with the labels on that class, and no disagreement on a destructive or network row is left unexplained. Where code agrees with the labels on every case, the question leaves the map entirely.

Step 7: Log the vector and every disagreement

Log the whole vector, not just the verdict. A record that says “ask” cannot tell you next month whether safe or risk tipped it. Each line carries the question-set version, every answer, the code-check results, the policy version and deciding branch, the reported model and the request ID. The full record, and why replaying policy over a stored answer differs from asking Jev again, is the decision-log piece.

{"ts":"2026-09-22T14:03:11Z","decision_id":"d-7f3a","lane":"billing-refactor","tool_call":"tc-0412",
 "qset":"tool-gate@3","policy":"tool-gate-policy@5","model":"jev-1.13.0","request_id":"req-from-response-header",
 "code":{"path_inside_repo":true},
 "vector":{"safe":{"noul":0.94},"on_task":{"noul":0.88},"risk":{"score":1.2,"confidence":0.71},"kind":{"choice":"write","confidence":0.83}},
 "decision":"ask","branch":"risk above 1 or uncertain","shadow":{"subagent":"allow"},"disagreement":true}

Disagreements are the product of this step: during the comparison, log every check where the arms disagree with each other or with the label. After cut-over, keep the old subagent or a human reviewer in shadow on a small slice and keep logging. Review weekly and give each disagreement one cause: criteria wording, a missing state field, a threshold, a policy bug or a wrong label. The fix lands as a new question-set or policy version, never a silent edit.

Where a Jev API questions vector fails, and the signal for each

A missing answer read as zero. A default fills the gap and the gate passes. Signal: validation failures with no matching ask or deny. Fix: step 5 fails the whole vector; defaults are banned.

Arithmetic across answers. Code subtracts one Noul from 1 to stand in for another, or sums related Nouls. Signal: any policy line that folds two probabilities into one number.

One threshold for two primitives. Someone rewords a Noul as a Choice and the old number rides along. Signal: a question-set version bump with no policy version bump.

Question creep. The map grows because questions are cheap, and half feed no policy line. Signal: IDs the policy never references, and a longest question creeping toward 32K.

The door changed under the policy. A lane moves to a gateway that says boolean, and answers["safe"].noul goes missing. Signal: validation failures spike right after a routing change.

Adversarial state. A diff written to argue for its own classification can move the answer, as the jaggedness page warns. Signal: nothing in the vector looks wrong. Fix: code allowlists first, the sandbox behind, and injected fixtures in the labeled set, as the injection piece lays out.

The decision vector is fleet policy, not a prompt

Nothing in this runbook asks a model to be careful. The question map, thresholds, budget check, validator, policy function and disagreement log are files and code outside every model, applied the same way on every lane whichever CLI runs it. That is what makes the vector auditable: one version of one policy, fed by one typed call, with a log that names the deciding branch.

It is also why the vector belongs in the layer that runs the fleet, not in any one agent’s instructions. A multi-agent command center is where lanes, gates, kill switches and evidence meet; a decision vector per gate is one more thing that layer owns, and one fewer session it pays for.

FAQ

Can I ask Jev several questions in one API call?

Yes. One Jev request carries a state plus a map of questions keyed by IDs you choose, and answers return under the same IDs. Jev ingests the state once and evaluates every question in parallel. The state plus all questions must fit in 64K tokens, and the state plus the longest question in 32K.

Can Jev replace a yes/no reviewer subagent?

For checks that only return a label, yes, once you measure it. Keep exact checks like paths and counts in code, and leave merge readiness to a reviewer model or a person. Retire the subagent for a check class only when Jev matches or beats its agreement with your labels on that class.

How do I combine several Jev answers into one decision?

Combine them in code, in a policy function that reads each answer by its ID against its own threshold. Answers are independent, so don’t derive one from another or sum related Nouls; TypeSafe’s example of a question and its negation summed to 1.19. Any missing or invalid answer should end in ask or deny.

Sources