# Design Jev decisions in AgentRun

Jev returns typed judgments and probabilities, not generated prose, tool calls or
multi-step research. The host supplies its adapter and credentials; the workflow
names the question. Do not write
HTTP requests or SDK setup into workflow code. This guide maps the TypeSafe
programming model to the installed DSL; the general TypeSafe skill is optional.

## Choose the boundary

Work backward from the action the result controls. Code handles exact lookups,
parsing, arithmetic, joins, grouping and constraints. An agent handles open-ended
research or generation. Jev can select relevant evidence, interpret a boundary,
compare a claim to its source or choose among known actions. It need not be a
final approval stage, and a workflow without a semantic decision need not use it.

Ask one coherent judgment per property. Separate independently useful conditions;
keep related context together. Avoid asking whether a whole complex answer is
"correct" when no source supporting that answer is supplied. Question descriptions
must carry their meaning; property names alone are not instructions.

## Construct the evidence, not another opinion

For source-grounded work, a useful pattern is:

1. A tool or agent locates candidate references, retaining their identities.
2. Direct tool calls fetch the original text at those references.
3. Jev sees the question or full selection rule **and that original text**.
4. Code uses the returned values; an agent may repair missing evidence or synthesize.

Keep source text separate from a candidate extraction, paraphrase or claim. An
agent's field named `quote`, `headerContext` or `verified` does not make it original
evidence. Re-read the cited source through the host tool when independent checking
matters. Include relevant headings, units, dates, neighboring rows, exclusions and
section boundaries. An exact quote match checks presence, not interpretation.

Judge only what the supplied context can establish. If a source is truncated,
follow its continuation when relevant; if it is missing, retrieve or surface the
gap. Do not silently substitute a summary to fit a size limit. Narrow or partition
large contexts at meaningful boundaries while retaining their qualifiers.
The adapter has no default byte ceiling. A host may explicitly configure
`maxStateBytes`; local size errors then report the measured UTF-8 JSON bytes and
that host's limit before any HTTP request is sent. Provider context limits are
separate and token-based, not a fixed bytes-to-tokens conversion. Jev 1.13's
documented contract, checked on 2026-09-22, allows 32k tokens for state plus the
longest question and 64k for state plus all questions combined; check the selected
model's current contract. Oversized provider requests remain
errors, not permission to truncate evidence or silently retry a different graph.

`sift` sends its items together, in as few requests as the host's limits allow:
when the items exceed `maxQuestionsPerRequest` or the host's
`maxStateBytesPerRequest`, it splits them in order into requests that each fit.
Shared `state` is added to every request; it does not replace or narrow each item.
`describe` adds an item summary, but the full original item is still sent. A tool envelope can contain
the same text in both `content` and `details`, so character limits on individual
reads do not establish the final serialized size.

When judgments are independent, explicitly use `map` with a `judge` body for one
request per original. For records that already contain `document`, `page` and
`text`, a body can use `state: {rule: "{question}", original: "{item}"}`. For tool
envelopes, select the host's actual original-text and provenance fields instead
of sending duplicate representations. Retain the full reviewed rule, relevant
headers, units, dates, exclusions and neighboring rows. If an individual original
is still too large, retrieve or partition it at meaningful boundaries; do not
silently truncate it or replace it with an agent's interpretation. This explicit
graph change changes request count; nothing is truncated.

The fictional [read-source decision](../examples/read-source-decision.json) and
[input](../examples/read-source-decision.input.json) show direct tool results passed
to a judge unchanged, followed by deterministic probability projection in code. Its
paths are inputs, not a built-in corpus: place the bundled fictional
[release note](../examples/release-note.txt) in the working directory before running
that input. Adapt the tool and its
return schema to the tools the host actually names; an agent locator can produce the
references earlier in the same graph.

## Use the installed primitives

- `judge`: a flat output schema describes several independent questions over one
  explicit `state` map. Boolean properties become yes-probabilities (Noul); enums
  become Choice; integer properties with minimum 0, maximum N−1 and N described
  levels in `criteria` (2–10 levels) become Score. Put the complete question in each
  `description`.
- `sift`: ask questions about each candidate and retain originals. Pass shared task
  context in `state`; each item also needs its own original evidence. Candidate
  coverage matters: no judge can recover a source absent from the candidate set.
- `pick`: choose from known candidates, with `allowNone` when none may fit.
- `route`: run one of several branches. With `instructions` and `state`, Jev
  chooses a semantic branch, and its `unsure` gate concerns that new decision, not
  an earlier judge's probability. With `valuePath`, the workflow already holds the
  choice and no question is asked.

Route by value when the decision exists. A common shape: a `judge` answers the
question, a `code` step applies your thresholds to its raw probabilities and
writes an action string, and a `route` with `valuePath` runs that action's branch.
Asking a second `route` instead costs another request and can choose differently
from the judge, past your thresholds. Use Jev where the answer needs judgment and
code where it is certain from the data: exact matches, lookups, arithmetic and a
threshold already decided.

Batch independent questions over the same state. They cannot read each other's
answers. Use another step when a decision is needed to retrieve new evidence or
construct new state. Inspect the assembled state and questions, not just node names.

## Name the provider, pin the model, bound the time

A decision node (`judge`, `pick`, `sift`, a `route` Jev chooses, or an `ask`
predicate) may say five things about the provider that answers it. Each is optional:

- `provider`: the id of one of the host's decision providers, such as `jev`,
  `parallel` or `openrouter`. Absent, the host's default answers. An `openrouter`
  node names its OpenRouter Decisions model in `model` (such as `liquid/d1`).
- `model`: a model or version to pin, so a run asks the same model next month. A
  changed pin is a different decision: its answer is never replayed from another
  model's receipt. A provider that declares the models it serves refuses any
  other pin before the run starts; Parallel serves `parallel-fast` only.
- `evidence`: `"off"` keeps none; `"if_supported"` keeps what the provider gives;
  `"required"` needs some. A provider whose capabilities declare no evidence is
  refused for a `"required"` node before the run starts, and an answer that comes
  back without any fails the request.
- `timeoutMs`: the node's own bound, at most 120000. It only shortens the
  provider's deadline, never lengthens it. A call cut by it after it was sent may
  have been answered and billed: it is not asked again, and the run goes to review.
- `images`: `{"<page1>": "{item.pages.0.image}"}` sends images with the request.
  Each token must appear literally in the node's `state` or in a question's text,
  where the image belongs (`"Does <page1> carry a seal?"`). Each reference is
  written as a state value is and resolves to an image path the host reads; the
  bytes never enter state. A provider without image input is refused before the
  run. The receipt names each image by its SHA-256, never its bytes, and keeps the
  state the judge saw. On `openrouter` with a model other than Jev (such as
  `liquid/d1`), the token must stand alone as a top-level `state` value
  (`"page": "<page1>"`), never inside text or a question: those models read an image
  only as a whole state item. Jev on TypeSafe pins `jev-1.14.0`; through OpenRouter,
  `typesafe/jev-1.14`. Jev takes an image of at most 1568 px on each side (a letter
  page renders at 140 dpi to 1190x1540); a larger one is refused before it is sent.

The host checks each decision node against its providers before any node runs: a
provider it lacks, evidence it cannot give, a model it does not serve, a question
type it does not answer, or images it cannot take stops the run with the node
named. A host that names no decision providers cannot honour `provider` or
`timeoutMs`, so it refuses a node that sets either. The evidence a provider gives
travels with the answer, in `<as>$answers.evidence`, the decision receipt and the
node's event.

## Consume uncertainty honestly

`judge` stores decoded values at its `as` key and raw answers at `<as>$answers`.
A boolean is true at yes-probability 0.5; to enforce another threshold, read the
raw `.answers.<property>.noul` number and compare in code. A Noul near 0.5 means
uncertainty between yes and no, not medium intensity. Choice/Score confidence is
distribution concentration, not proof that the workflow is right.

Use thresholds appropriate to the task, with an explicit missing/uncertain path.
An example threshold is not calibrated policy. Low probability can justify
exclusion, another retrieval, cheap-agent repair or escalation, depending on the
question. Do not reject an entire result just because an unused condition is
uncertain. Preserve raw decisions so these policies can be inspected separately.

Typed output proves an interface, not truth. Test source coverage, semantics and
the downstream action independently. A fake-judge fixture proves wiring only.

Maintained against TypeSafe's state, primitives/Noul, confidence and citation-check
documentation, reviewed 2026-09-22. Upstream reference locations:
`https://docs.typesafe.ai/concepts/state`, `https://docs.typesafe.ai/primitives`,
`https://docs.typesafe.ai/confidence`, `https://docs.typesafe.ai/cookbooks/citation_check`.
