Every major architectural shift in software engineering forces us to change how we model the world. When the relational database replaced hierarchical file storage, we stopped thinking in nested records and started thinking in sets, tuples, and predicates. When HTTP replaced bespoke socket protocols, we stopped thinking in long-lived connections and moved to stateless request/response cycles.
Jev’s three primitives—Choice, Score, and Noul—represent a paradigm shift of equal magnitude. They require engineers to stop thinking in terms of generated text and start thinking in terms of evaluated propositions.
This is not a stylistic preference. It is a direct consequence of shifting away from brittle text-generation pipelines and toward System One models with native calibration. To build a robust production decision engine, you must first internalize why there are exactly three primitives, what each one is fundamentally asking the machine to do, and how they compose into scalable, sub-100ms workflows.
The concepts and code demonstrated here are drawn directly from my ebook Jev: The Definitive Guide to System One AI here. Check also the 9 volumes bundle: TypeScript AI & Agentic Engineer Masterclass
The Four Fatal Flaws of Generative AI in Software Decision-Making
The naive application of generative AI to software decision-making usually starts with a prompt like this:
"You are a helpful support assistant. Read the following ticket and reply with the category as one of: billing, technical, or sales. Reply with only the category name."
This prompt introduces four layered mistakes:
- The Structural Mistake: You are asking a system trained to produce natural language to output a single word—a degenerate case of natural language where probability distributions fail to concentrate reliably.
-
The Epistemic Mistake: The model cannot communicate its confidence. It emits
billingwith the exact same token-stream certainty whether it is 99% confident or a coin-flip 51% confident. - The Compositional Mistake: If you later need to ask a second question about the same ticket (e.g., "Is this urgent?"), you must either double your round-trip latency with a second API call or stuff both questions into the same prompt, inviting context rot.
- The Operational Mistake: Parsing model outputs back into typed application values requires brittle regular expressions, exception-handling for markdown code blocks, and constant adjustments for conversational filler. You aren’t writing software; you are building a fragile adapter between a text generator and a type system.
Jev's three primitives eliminate all four mistakes by inverting the relationship. Instead of asking a generative model to emit a decision as text, you ask a System One model to evaluate a decision and return a typed probability distribution. The model produces a choice, a score, or a noul (a floating-point number in [0, 1]). Your application's type system becomes the direct target of the model's output.
The Web Development Analogy: HTTP Methods and a Routing Table
Before examining each primitive individually, it helps to view the trio as a single coherent interface. The best mental model is the HTTP method set on a REST API.
A well-designed REST API does not expose a single do_something endpoint accepting arbitrary strings. It exposes a closed vocabulary of named methods—GET, POST, PUT, DELETE, PATCH—each carrying a specific semantic. The URL path tells the server what the operation targets, and the method tells it what kind of operation it is.
Jev's primitives work identically:
-
A selection among a finite set of named alternatives —
Choice. This is theGET /departments/{name}of the primitive world. -
A placement on an ordered spectrum defined by a rubric —
Score. This is thePUT /severity/{level}: assigning an ordinal position with defined meanings at every step. -
The truth value of a single proposition —
Noul. This is theHEAD /proposition: a stripped-down binary check returning a probability.
Just as /users/42 can be GET'd, PUT'd, or DELETEd, a single support ticket can be Choice'd (which department?), Score'd (how frustrated?), and Noul'd (was a refund requested?) all in a single parallel request. The primitives are not competing options; they are the complete palette.
The Three Shapes of Epistemic Questions
Why exactly three primitives? The answer lies in measurement theory, which classifies every observable property of a system into scales:
- Nominal: Names or categories with no inherent order (department, country).
- Ordinal: Values with an order, but no defined distance between levels (severity levels).
- Interval / Ratio: Quantifiable continuous values.
Jev's primitives map onto the first two. A Choice operates over a nominal space. A Score operates over an ordinal space. A Noul sits at the boundary, evaluating a binary proposition {true, false} which uniquely admits a scalar probability interpretation.
Attempting to make a semantic model output arbitrary interval-or-ratio data (like a physical sensor reading) forces a semantic judgment through a numerical bottleneck the model does not possess.
Deep Dive: The Three Primitives
1. Choice: The Primitive of Categorical Commitment
The Choice primitive answers "Which of these?" against a closed set of named alternatives.
-
The option space is fixed and exhaustive by construction. You supply a
criteriamap whose keys are possible outcomes. The model returns a probability distribution over those exact keys. Your TypeScript type and the model's output space are guaranteed to be identical. -
Criterion descriptions are boundaries, not labels. The description text operates as a fence around each option. Writing clear boundaries—and adding
not_fornotes—prevents the model from confusing neighbouring categories. -
The output is a probability distribution. While
choicegives you the winning label, theprobabilitiesmap andconfidencescore reveal whether the model made a definitive call or a coin-flip.
2. Score: The Primitive of Gradient Judgment
The Score primitive answers "Where on this spectrum?" against an ordered list of level descriptions.
Under the hood, a Score evaluates a probability distribution over discrete levels, returning a probability-weighted mean. If your levels are 0, 1, and 2, and the model returns probabilities {0: 0.1, 1: 0.6, 2: 0.3}, the resulting score is 1.2.
Crucially, a Score question is not a slider. The model matches the state against your descriptive scenarios, not against numbers. Rubrics must be written as distinct, mutually exclusive situations.
3. Noul: The Primitive of Propositional Truth
"Noul" is a portmanteau of "null" and "boolean" denoting a probability in [0, 1] that a proposition is true.
A Noul is the strictest of the three primitives because it forces the model to evaluate an absolute probability about a single proposition, independent of any sum-to-1 constraints across other categories. Whenever you need a yes/no gate—Is this input a jailbreak? Does this text contain PII?—Noul is the correct instrument.
Practical Implementation: A Production Triage Module
Let's look at how these concepts translate into production TypeScript using @typesafe-ai/sdk. Below is a complete support triage module for a Next.js application.
// lib/jev/triage.ts
import {
choice,
noul,
score,
TypeSafeClient,
type ChoiceResponse,
type NoulResponse,
type ScoreResponse,
type SystemOneResult,
} from "@typesafe-ai/sdk";
const client = new TypeSafeClient({
apiKey: process.env.TYPESAFE_API_KEY!,
});
export const TRIAGE_QUESTIONS = {
department: choice("Which team should handle this ticket?", {
billing: "Payment, invoicing, refunds, or subscription issues.",
technical: "Bugs, outages, integrations, or API errors.",
sales: "Pricing, plan upgrades, or new account questions.",
}),
frustration: score("How frustrated does the customer appear?", [
"Calm; stating facts without complaint.",
"Frustrated but civil; expresses annoyance.",
"Very angry; strong language or threatens to leave.",
] as const),
refund_requested: noul("Does the customer request a refund?", {
true: "The customer explicitly asks for money back.",
false: "No refund is requested.",
}),
};
export type TriageResult = SystemOneResult<typeof TRIAGE_QUESTIONS>;
export async function triageTicket(message: string): Promise<TriageResult> {
return client.systemOne({
state: { message },
questions: TRIAGE_QUESTIONS,
});
}
Now, let's consume this module inside a Next.js API route handler, utilizing confidence gating and probability thresholds:
// app/api/triage/route.ts
import { NextResponse } from "next/server";
import { triageTicket } from "@/lib/jev/triage";
export async function POST(request: Request) {
const { message } = await request.json();
// All three questions run in parallel in a single round-trip (~100ms)
const response = await triageTicket(message);
const { department, frustration, refund_requested } = response.answers;
// Confidence gating: low confidence routes to a human reviewer
const owned = department.confidence < 0.5;
const route = owned ? "human_review" : department.choice;
// Threshold Noul probabilities for hard security or business gates
const wantsRefund = refund_requested.noul > 0.7;
// Evaluate fractional Score positions
const needsPriority = frustration.score > 1.5;
return NextResponse.json({
route,
department: department.choice,
departmentConfidence: department.confidence,
frustrationScore: frustration.score,
needsPriority,
wantsRefund,
probabilities: department.probabilities,
model: response.model,
usage: response.usage,
});
}
Line-by-Line Architecture Breakdown
-
SDK Initialization & Environment Safety: Instantiating
TypeSafeClientwith an explicit API key guarantees fast feedback if your environment variable is missing during module load. -
Single Source of Truth:
TRIAGE_QUESTIONSacts as the single declaration point for both the network payload and the TypeScript types. -
Parallel Execution: When
client.systemOne()fires, all questions are evaluated simultaneously against a frozen state snapshot. Adding a fourth question incurs virtually zero additional latency. -
Confidence-Gated Control Flow: By checking
department.confidence, you protect your system from hallucinated classifications. If the model is guessing, your code gracefully falls back to human review. -
Score Normalization: Scores return fractional values across a rubric. When combining multiple scores into a composite priority index, always normalize them to a
[0, 1]range by dividing by the max level index (criteria.length - 1).
Advanced Composition: Building a Complete Triage Pipeline
In production applications, you rarely rely on a single primitive. You compose them alongside deterministic pre-filters and code-level policy guardrails. Here is a complete enterprise routing function demonstrating the two-speed architecture:
import { NextResponse } from "next/server";
import {
RateLimitError,
TypeSafeClient,
choice,
noul,
score,
type SystemOneResult,
type ScoreResponse,
} from "@typesafe-ai/sdk";
export type Ticket = {
id: string;
subject: string;
body: string;
plan: "free" | "pro" | "enterprise";
priorTickets: number;
status: "open" | "pending" | "closed";
};
export type Team = "billing" | "technical" | "account";
export type Decision =
| { action: "route"; team: Team; priority: "high" | "normal" }
| { action: "quarantine" | "escalate_risk" | "human_review" | "no_action"; reason: string };
const TRIAGE_QUESTIONS = {
topic: choice("Which team should handle `ticket.message`?", {
billing: "Charges, invoices, refunds, plan changes, failed payments",
technical: "Bugs, outages, integrations, API errors",
account: "Login, profile, permissions, security settings",
}),
requestsCredentials: noul(
"Does `ticket.message` ask the recipient to disclose a password, one-time code, or API key?"
),
mentionsChargeback: noul(
"Does `ticket.message` state that the customer has disputed, or will dispute, the charge with their bank?"
),
frustration: score("How frustrated does the customer appear?", [
"Calm and matter-of-fact",
"Frustrated but civil",
"Very angry, or threatening to leave",
] as const),
impact: score("How much of the customer's work does this block?", [
"No impact — a question or preference",
"Degraded — a workaround exists",
"Blocked — a core feature is unusable",
] as const),
} as const;
const POLICY = {
credentialVeto: 0.85,
chargebackVeto: 0.75,
topicConfidenceFloor: 0.75,
weights: { impact: 0.6, frustration: 0.4 },
} as const;
const jev = new TypeSafeClient();
type TriageAnswers = SystemOneResult<typeof TRIAGE_QUESTIONS>["answers"];
const normalize = (s: ScoreResponse<readonly string[]>): number =>
s.score / Math.max(Object.keys(s.legend).length - 1, 1);
function compose(a: TriageAnswers): Decision {
// 1. Vetoes take absolute priority over preferences
if (a.requestsCredentials.noul >= POLICY.credentialVeto) {
return { action: "quarantine", reason: `Credential request probability: ${a.requestsCredentials.noul}` };
}
if (a.mentionsChargeback.noul >= POLICY.chargebackVeto) {
return { action: "escalate_risk", reason: `Chargeback mention probability: ${a.mentionsChargeback.noul}` };
}
// 2. Uncertainty gate on classification
if (a.topic.confidence < POLICY.topicConfidenceFloor) {
return { action: "human_review", reason: `Low routing confidence: ${a.topic.confidence}` };
}
// 3. Composite score calculation in deterministic code
const priorityScore =
POLICY.weights.impact * normalize(a.impact) +
POLICY.weights.frustration * normalize(a.frustration);
return {
action: "route",
team: a.topic.choice as Team,
priority: priorityScore >= 0.6 ? "high" : "normal",
};
}
Common Pitfalls to Avoid
-
Parsing Generated Prose: If your codebase contains regular expressions parsing markdown or free-form JSON from an LLM, you are building brittle adapters. Switch to
Choiceand let the SDK guarantee type safety. - Ignoring Confidence Thresholds: Never act automatically on low-confidence classifications. Always establish a confidence floor below which tickets fall back to human review.
-
Conflating Noul with Magnitude: A Noul measures binary truth probability (
[0, 1]), not scalar degree. If you need intensity, use aScore. -
Serializing API Calls: Never loop through items sequentially. Leverage
Promise.all()to batch independent evaluations and maximize throughput.
Conclusion
Jev's three primitives—Choice, Score, and Noul—provide the foundational measurement scales required to turn probabilistic AI models into deterministic software components. By replacing unstructured text generation with typed probability distributions, you decouple semantic judgment from application control flow.
When you build systems this way, the model becomes what it was always meant to be: a high-speed, calibrated semantic preprocessor sitting at the edge of your architecture, leaving complex business logic, validation, and policy enforcement safely inside your application code where it belongs.
The Computing & AI Pioneers Bundle: is a great read after a long day of coding!
Top comments (0)