Status: Design — not implemented, and not yet evaluated. Everything below about Jev is taken from TypeSafe AI's announcement post (introducing-system-one-models-and-jev, 2026) plus the existence of
docs.typesafe.aiand thesystem-one-adapter-pythonwrapper. The API surface has not been exercised; the latency, calibration and cost figures are the vendor's claims, not measurements. Read this as "what the integration would be if the claims hold", and treat the worked example in Second realization as the experiment that would decide it.Companion: decision-semantics.md, adapter-orc-execution-bridge.md.
Jev is a "System One Model": it takes program state and returns a typed value from a schema fixed in advance, with a calibrated probability attached. It cannot emit free text or code. Its stated shape is a closed output alphabet of at most 255 choices, 70–500ms end to end, $0.042/MTok in with output tokens free.
That is not a language model with a JSON mode bolted on. It is an engine
for exactly the artifact atlas.ontology.decision already describes.
:llm:decision/logic-style currently names four traditions: :predicate,
:table, :llm, :code. The operative guidance (from the
skill-as-decision-tree work) has been: keep discriminators :table or
:code — cheap, auditable, and structurally unable to invent an outcome
outside the alphabet — and spend :llm only where no table can work.
That guidance has a hole in it. A :table cannot read a paragraph. An
:llm can, but it pays for that with latency, cost, and the standing
possibility of an answer off the alphabet. So every decision whose inputs
are unstructured but whose question is closed has been forced onto the
expensive side of the line.
A System One model sits in that hole:
| structured input | unstructured input | |
|---|---|---|
| closed question | :predicate / :table — auditable, free | the hole → Jev |
| open question | :code | :llm |
The trade is explicit and worth naming: a table tells you why it decided; Jev gives you a confidence number instead of a reason. It buys alphabet safety and speed at the cost of the audit trail a table hands over for free. So the rule stays: table where the evidence is already structured; Jev only where it genuinely is not.
The ontology and the model line up field for field — not by design on either side, which is the interesting part:
:atlas/decision | Jev |
|---|---|
:decision/outcomes — closed set, ≥ 2 (one outcome is a computation) | the output schema's choices, ≤ 255 |
:decision/inputs — declared data-keys | "structured program state" as input |
:decision/question | the query |
:decision/logic-body | the instruction / trained policy |
:decision/realized-by | the call site |
:decision/mandated-by — the authority | nothing — Jev has no equivalent |
| nothing — atlas has no home for this | the calibrated probability |
Both ends of the cardinality constraint are already enforced somewhere:
:invariant/decision-outcomes-closed rejects an alphabet below 2, and Jev
caps at 255. Together they are a single checkable range, and the adapter
should check it at emit time rather than discovering it in a 4xx.
The two blank rows are the whole substance of the collaboration. Atlas has governance Jev lacks; Jev has calibration atlas has nowhere to put.
Pure projection, like Overarch and YAML-LD, not an ingest-and-scaffold arrangement like Allium. Atlas owns the decision; the adapter renders it as a Jev request; nothing flows back into the registry as vocabulary.
(jev/emit-decision :decision.review/ci-gate)
;; =>
;; {:query "Do these diff statistics warrant blocking the build?"
;; :schema {:type :choice
;; :values [:verdict/pass :verdict/block]} ; ← :decision/outcomes, verbatim
;; :state {:diff-stats ...} ; ← :decision/inputs, projected
;; :instruction "Given created/changed/deleted counts, block if ..."} ; ← :decision/logic-body
The point is that the schema stops being hand-maintained. Today an
outcome alphabet lives in the registry and again, by hand, in whatever
engine executes it; they drift, and nothing notices. Emitting from
:decision/outcomes makes drift impossible in the same way the ORC bridge
made it impossible for behaviour trees.
Honest gap in the emit. Atlas declares data-key identity, not data-key
shape — schemas are malli's job, one layer down (see the artifact table in
adapter-allium.md). So the output side of the
request is fully derivable from the registry, and the input side is only
partially: atlas knows which keys are in scope and what produces them, not
what they contain. Either the projection function is supplied per decision,
or the adapter reads :atlas/data-schema entities where they exist. Do not
pretend the input side is free.
:jev to coreThe obvious move is to widen the :decision/logic-style spec set to
#{:predicate :table :llm :code :jev}. That is the snap-005 leak again, in
a new coat: atlas.ontology.decision is a seam — engine-neutral by
construction, requiring only registry/lookup/type-ref — and a vendor's name
in a core spec is downstream vocabulary walking across a boundary the code
respects. The module rule is already written down
(ontology-extensibility.md): seams in core,
specializations downstream.
Recommended instead:
:model (a trained
classifier decides; the logic body is an instruction, not an inspectable
mapping). That is a genuine new tradition alongside decision tables and
production rules, and it would have been worth adding even if Jev did not
exist.:jev/* aspects on the realization
entity, never on the decision. The decision says "a model decides this";
the realization says "specifically, Jev does."This also forces the limitation already logged against the ontology: a
decision with a :table realization and a :model realization has two
logic-styles, and :decision/logic-style is single-valued. Adding a fast
model makes that bite immediately, because running both engines against one
question is the good configuration, not a transitional state — see below.
Per-realization logic-style stops being a "candidate future fix".
Jev reports how confident it is. It has no idea what the decision is
permitted to do. Atlas does — :decision/mandated-by an authority,
:decision/mitigates a failure mode, invariants over the graph.
Two additions carry that:
;; on the decision, not the realization — the threshold is a property of the
;; question and its authority, not of whichever engine answers it
{:decision/confidence-threshold 0.9
:decision/below-threshold :verdict/escalate} ; must be IN the alphabet
and the invariant that makes it mean something:
:invariant/model-decision-guarding-writes-needs-threshold(error) — a decision whose logic-style is:modelmust declare:decision/confidence-thresholdand a:decision/below-thresholdoutcome if any consumer of its verdict carries:effect/write.
That is the question "where in this architecture are probabilistic decisions allowed to act unsupervised" — squarely atlas's job, and nobody else's. A fast, cheap, structurally-safe model makes it more urgent, not less: the cost of putting one in a write path drops to nearly zero, so the only remaining brake is a declared one.
:invariant/decision-realization-covers-inputs was written after the
false-approve bug: a leaf read [:bb/diff-summary] while the decision
declared three richer inputs, a 404 degraded to
{:from-count nil :changed-count 0 :deleted-count 0}, and the judge
confidently approved a version that did not exist. The lesson recorded then
was that an LLM judge cannot rescue a broken deterministic upstream.
A System One model does not change that and makes it cheaper to commit. At 200ms and effectively free, the temptation to point a decision at a thin projection is much stronger than at 1s and $0.0005. The warning that fires on it is already written and already negative-tested. This is the concrete safety argument for putting a registry in front of a fast model, and it costs nothing — it is running today.
:decision.review/ci-gate (in examples/grain/src/orc_demo/decisions.clj)
is the right first experiment, because it is small, already built, already
run against real data, and deliberately scoped to a thin question:
{:decision/question "Do these diff statistics warrant blocking the build?"
:decision/inputs [:bb/diff-stats]
:decision/outcomes #{:verdict/pass :verdict/block}
:decision/logic-style :llm
:decision/realized-by #{:bt.review/judge-verdict}} ; :ai leaf, haiku, ~438 tok / ~$0.0005 / ~1s
Add a second realization on Jev — same question, same alphabet, same
declared inputs, different engine. That is the engine-neutrality claim the
ontology was built to make (:decision/realized-by is cardinality-many
precisely for this), exercised against a vendor rather than against a
second in-house executor.
What it yields beyond a working integration:
:verdict/pass and :verdict/block
have already been observed from real executions on the ORC side, so there
is a baseline to compare against rather than a vibe.Keep :decision.review/atlas-review-verdict — the rich review — out of
this. Its value is the document; the verdict is a one-word summary of work
a thin leaf does not do. Different inputs mean a different question. That
split was made deliberately and should survive the adapter.
decision-outcomes-closed gate is the same gate.:atlas/llm-prompt is untouched. Jev cannot generate text or code, so
/define-ontology, /author-decision, /atlas-review's prose — none of
it moves. The adapter replaces verdict leaves, not authoring. The model
receives verdicts as input; where it makes them, it makes only closed ones.core/src/atlas/adapter/jev.clj
follows the existing adapter precedent (allium, yaml-ld, overarch all sit
in core and all target third-party artifacts). Registry vocabulary does
not: :jev/* aspects belong on realization entities in a downstream
module. Code boundaries are enforced by deps.edn; registry boundaries are
not, which is exactly how :behavior-tree/llm got into atlas/core@snap-005.Worth stating, because it is the adoption bottleneck for any typed-decision
API: "which decisions in my system should be model calls, and what are their
alphabets?" is a registry query — entities carrying :atlas/decision with a
closed alphabet under 255 and a :table/:llm style. And "what breaks if
this alphabet changes" is consumers-of plus blast-radius, over
[?d :decision/outcome :verdict/reject] facts the extractor already emits.
Schema evolution for typed decisions is a problem any such service will
have, and it is one atlas already answers.
docs.typesafe.ai, whether confidence is per-outcome or scalar, and
whether the input side wants a flat map. Everything below depends on it.:decision/logic-style :model in core — engine-neutral, no vendor name.atlas.adapter.jev/emit-decision — alphabet → schema, with the
2 ≤ cardinality ≤ 255 check at emit time. Input projection supplied, not
inferred.:decision/confidence-threshold + :decision/below-threshold and the
write-path invariant — before any such decision reaches a write path,
not after.:model vs :classifier vs :probabilistic. :model is
shortest and least committal about the mechanism; :classifier is the
most honest about the closed alphabet.:decision/confidence-threshold belongs on the decision (the
authority sets the bar) or on the realization (each engine calibrates
differently). Leaning decision — it is a policy, not a tuning parameter.:decision/outcomes
(keeps the alphabet closed and total; costs every such decision an extra
outcome) or sits outside it as an escape hatch (keeps alphabets clean;
punches a hole in closedness). Leaning in-alphabet — closedness is the
property everything else rests on.Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |