Rules for LLMs and developers working with jev_dsl — the compile-time contract DSL, two-tier content-addressed cache, and execution runner for TypeSafe Jev (SystemOneSDK).
Core Principles
- Define all contracts at compile time (
use JevDSL): Never construct ad-hoc question maps at runtime or load.jsonprompt files when a contract can be declared withdefnoul,defchoice,defscore, ordefjev. Compile-time contracts validate question definitions viaSystemOneSDK.prepare/1during compilation and cacheJason.Fragmentwire payloads in:persistent_term. - Choice stab-clause symbols compile to atoms: In
defchoiceandchoice,positive -> "..."compiles to the:positiveatom soresponse.answers.tone.choice == :positiveandresponse.answers.tone.probabilities.positivework directly with standard Elixir pattern matching. Contract names, question names, and answer helpers (JevDSL.fetch_answer/2,JevDSL.choice?/2,JevDSL.probability/2) accept both atoms and strings. - Never hash
statewithout the contract fingerprint: Always compute cache keys viaMyContracts.cache_key(contract_key, state)orJevDSL.Cache.key(state, prepared). Editing a prompt or rubric criterion changesprepared.fingerprintat compile time and automatically invalidates stale cached judgments. - Always use
evaluate_many/3for batches: Never wrap singleevaluate/3calls inTask.async_stream/3(which bottlenecks on:httpcconnection serialization).MyContracts.evaluate_many/3resolves L1/L2 cache hits in bulk (get_many/2) and fans out only uncached misses viaSystemOneSDK.evaluate_many/4.
Canonical Usage (# ✅ DO vs # ❌ DON'T)
1. Defining Contracts (defnoul, defchoice, defscore, defjev, defalias)
# ✅ DO: Define contracts at compile time with use JevDSL
defmodule MyApp.TriageContracts do
use JevDSL,
otp_app: :my_app,
cache: JevDSL.Cache.ETS,
tier2_cache: MyApp.JevParquetCache
# Single boolean probability (0.0..1.0)
defnoul :is_spam, as: :spam_detected do
"Does this message contain unsolicited promotional spam?"
end
# Noul with explicit true/false criteria
defnoul :is_urgent, ask: "Does this ticket require immediate paging?" do
true -> "Production outage or active security breach."
false -> "Routine bug report or feature request."
end
# Mutually exclusive classification (stab clauses compile to atoms: :positive, :neutral, :negative)
defchoice :sentiment,
as: :tone,
ask: "Classify the overall tone of the message." do
positive -> "Constructive, appreciative, or favorable tone."
neutral -> "Factual or informational without strong emotion."
negative -> "Critical, frustrated, or dissatisfied tone."
end
# Ordered rubric grading scale (2..10 levels)
defscore :completeness,
ask: "Rate how complete the submission is." do
incomplete -> "Missing required identity or contact fields."
partial -> "Core fields are present but optional context is missing."
complete -> "All required and optional fields are populated."
end
# Multi-question bundle evaluated in a single HTTP request
defjev :ticket_triage do
noul :requires_escalation do
"Does this issue require immediate engineering escalation?"
end
choice :priority,
ask: "Select the appropriate response priority." do
urgent -> "Production outage or security incident."
normal -> "Standard bug report or feature question."
end
score :clarity,
ask: "Rate the clarity of the reproduction steps." do
vague -> "No reproduction steps provided."
clear -> "Includes clear steps and expected vs actual behavior."
end
end
defalias :triage, :ticket_triage
end2. Evaluating & Reading Answers
# ✅ DO: Pattern match directly on atom keys or use helpers
{:ok, response} = MyApp.TriageContracts.evaluate(:sentiment, %{text: "Great work!"})
%{choice: :positive, probabilities: %{positive: p}} = response.answers.tone
# Or use atom/string-safe helpers
{:ok, tone} = JevDSL.fetch_answer(response, :tone)
if JevDSL.choice?(tone, :positive) do
{:ok, ^p} = JevDSL.probability(tone, :positive)
end
# ✅ DO: Use evaluate_many/3 for concurrent batch evaluation
results = MyApp.TriageContracts.evaluate_many(:is_spam, [%{text: "a"}, %{text: "b"}])
# ❌ DON'T: Never wrap single evaluate/3 calls in Task.async_stream
Task.async_stream(states, &MyApp.TriageContracts.evaluate(:is_spam, &1))3. Testing with SystemOneSDK.Test, :eval_fn, and async: true
# ✅ DO: Isolate ETS tables per test for `async: true` and use SystemOneSDK.Test or :eval_fn
setup do
uid = System.unique_integer([:positive])
table = :"jev_test_#{uid}"
start_supervised!({JevDSL.Cache.ETS, name: :"ets_#{uid}", table: table})
{:ok, cache_opts: [ets_table: table]}
end
test "evaluates through SystemOneSDK.Test transport", %{cache_opts: cache_opts} do
client =
SystemOneSDK.Test.client()
|> SystemOneSDK.Test.stub(tone: {:choice, "positive", 0.92})
assert {:ok, resp} =
MyApp.TriageContracts.evaluate(:sentiment, %{text: "hi"}, [client: client] ++ cache_opts)
assert JevDSL.choice?(JevDSL.fetch_answer!(resp, :tone), :positive)
SystemOneSDK.Test.verify!(client)
SystemOneSDK.Test.close(client)
end