Welcome back. In the previous lesson, you built a decision-specific state payload: compact evidence such as a sanitized support report, account-access status, and relevant incident status. Now we turn that evidence into a bounded decision contract.
A Choice question asks Jev to select one value from a fixed set. The important design work is not writing "type": "choice"; it is defining options that mean different things, cover the states your application must handle, and each lead to a concrete next step.
By the end of this lesson, you will be able to define a Choice whose options are mutually exclusive and operationally actionable, using a support-routing example in TypeScript-shaped JSON.
A Choice is a decision contract, not merely a label set
Use a Choice when the answer must be one category from a finite set:
- Which team should own this ticket?
- Which first response path applies?
- Which allowlisted function should the application call?
- Which message category should the UI display?
Do not use it merely because you have several words available.
A useful distinction:
| Decision needed | Appropriate Jev primitive |
|---|---|
| “Does the customer explicitly request a refund?” | Noul |
| “How severe is the customer’s frustration?” | Score |
| “Which handling path should own this ticket?” | Choice |
A Choice can return only one selected option, but that does not make your categories logically exclusive by itself. If you provide these options:
login_problem
account_issue
technical_issue
other
then one report can easily fit the first three. Jev must still select one, but your application has not expressed a reliable business distinction.
A better design starts with the application consequence:
“What single next handling path should this ticket take?”
That wording makes the output useful to code. If two options would trigger the same handler, queue, or UI state, they are probably not different options yet.
The Typed answer interface below illustrates the visible outcome of a Choice: Jev selects one defined value and reports how strongly the available options competed. The design task comes before those metrics: each displayed option must have a clear operational meaning.

For example, yes, no, and escalate can be valid options for:
“Should this workflow close the case, continue automated handling, or send it to review?”
They are less suitable for the plain factual proposition “Was the issue resolved?” In that case, a Noul is usually the clearer primitive, and “escalate” belongs to your application policy around uncertainty.
Read TypeSafe AI’s Choice documentation to connect the conceptual contract to the request and response structure used by Jev.
In the introductory “Choice” section, read the fixed-set guidance. Notice the distinction between categories, spectra, and binary propositions. Then read the “Request structure” section from the paragraph beginning “Below is a request where the state is a support ticket from an online shoe store” through the criteria explanation. Focus on the fact that both option keys and option descriptions are visible to the model. Continue through “Response structure,” then read “Good practice: ask more than one question per call,” especially the coverage advice. For this lesson, concentrate on designing one excellent Choice; later lessons will batch independent questions.
Four tests for well-designed options
A robust Choice has four properties.
1. One decision axis
Each option must answer the same question.
For example, these are different axes:
- ownership: billing, shipping, account support;
- requested resolution: refund, exchange, information;
- tone: calm, frustrated, angry;
- handling path: send incident update, route to account recovery, human review.
Do not mix them in one Choice:
billing
angry_customer
refund
human_review
Those values are not competing answers to one question. A billing ticket can also be angry and request a refund. Instead, keep the primary routing Choice narrow, then ask independent questions for tone or requested resolution when the workflow needs them.
2. Pairwise exclusivity
For each pair of options, write down why an input belongs to one and not the other.
This is contrastive design. It is especially important around adjacent concepts:
| Weak distinction | Why it fails |
|---|---|
return_policy versus return_status | Both may mention “return” and “refund.” |
service_incident versus account_problem | A user may report an outage while also having a locked account. |
standard_support versus human_review | Both can become vague “everything else” buckets. |
The remedy is not simply longer descriptions. It is defining explicit boundaries and, where required, policy precedence.
For instance:
- An account recovery path applies when an authoritative local status says the account is locked or suspended.
- An incident update path applies when a relevant active incident exists and the report matches its impact.
- A standard support path applies when the report does not establish a current core-function block.
- Human review applies when a current blocker is reported but the available evidence does not support any automated path, or evidence conflicts.
These are distinguishable because they use different evidence and lead to different actions.
3. Operational consequence
For every option, complete this sentence:
“When Jev selects this option and automation is permitted, the application will…”
If the sentence ends with “do something appropriate,” the option is not ready. The action can be a queue assignment, an allowlisted function invocation, a safe customer message, or a review task.
A safe review path is operationally actionable. It is not a failure of design. It says, precisely: this evidence is insufficient for an automated disposition, so create a human task and preserve the reason.
4. Coverage with a deliberate fallback
Your options should cover realistic inputs. In a taxonomy such as product categories, an other option may be necessary. But other is only useful if code knows what to do with it.
For a handling-path Choice, a purposefully named human_review is often better than an unexplained other, because it states the action and the reason it exists. Avoid an “other” bucket that silently routes cases into a default automated behavior.
A useful design rule is:
Use
otherfor an unmodelled category. Usehuman_reviewfor a case where the application must not automate.
Read the question-design guidance in TypeSafe AI’s “How to build with TypeSafe” guide. It explains why narrow questions and contrastive criteria make decisions easier to inspect and tune.
In step 4, “Decompose the questions,” read the atomic-question rationale. Then move to step 5, “Use structure in the questions,” and read the contrastive-criteria guidance. The longer code example below those steps is worth skimming after this lesson: its topic Choice demonstrates a practical routing decision whose options map to different teams.
Build a support-handling Choice from the previous state
Last lesson’s state had the relevant ingredients:
{
"report": "After resetting my password twice, I receive error AUTH_403 and cannot open my workspace.",
"product_area": "workspace_access",
"account_access": "locked",
"known_service_incident": "none"
}
Suppose the business needs a single first handling path. We can define this contract:
| Option | Meaning | Deterministic application action |
|---|---|---|
account_recovery | An account restriction or credential-recovery issue requires the account-support flow. | Create an account-recovery task or enter the verified recovery workflow. |
incident_update | A known relevant incident explains the reported unavailability. | Send the approved incident-status response and subscribe the ticket to the incident. |
standard_support | The report does not establish that the user is currently blocked from a core function. | Route to the normal support queue. |
human_review | Evidence is missing, contradictory, or reports a blocker with no safe automated explanation. | Create a review task; do not send an automated resolution. |
This has an intentional precedence order. A locked account takes precedence over an incident update because the account restriction is an authoritative fact that needs its own workflow. In a mature system, an absolute rule such as “locked account always enters recovery” can be handled in deterministic code before calling Jev. The Choice still documents a coherent disposition when the model participates in selecting the path.
Here is the question definition. This is the nested question object you would pass through the JavaScript SDK or place under questions.access_disposition in an HTTP request.
const supportDispositions = [
"account_recovery",
"incident_update",
"standard_support",
"human_review",
] as const;
type SupportDisposition = (typeof supportDispositions)[number];
const accessDispositionQuestion = {
type: "choice",
instructions: {
question:
"Which single first handling path should this support ticket take?",
inspect: [
"`report`",
"`product_area`",
"`account_access`",
"`known_service_incident`",
],
focus:
"Select the safest actionable path. Use human review when a current core-function access failure lacks a clear supported path.",
},
criteria: {
account_recovery: {
what:
"The account is locked or suspended, or the report is clearly a credential-recovery issue requiring the account-support flow.",
not_for:
"A known active service incident that explains the problem when the account has no restriction.",
examples: [
"My account was locked after too many sign-in attempts.",
"I reset my password but access is still blocked and the account status is locked.",
],
},
incident_update: {
what:
"A known active incident relevant to the product area matches the reported unavailability.",
not_for:
"A locked or suspended account, or a general product question with no current outage symptom.",
examples: [
"The workspace is unavailable while a workspace-access incident is active.",
"Document editing fails during the active editor outage.",
],
},
standard_support: {
what:
"The report is a normal question or non-blocking request and does not establish that the user currently cannot use a core function.",
not_for:
"An explicit current access failure, a locked account, or a report matching an active incident.",
examples: [
"How do I change my workspace display name?",
"Where can I find the document version history?",
],
},
human_review: {
what:
"The state is incomplete or conflicting, or the report describes a current core-function failure without a supported automated explanation.",
not_for:
"A clear account restriction, a clearly matching active incident, or a non-blocking normal-support request.",
examples: [
"I cannot access the workspace, but account status and incident status are unknown.",
"The account is active and no incident is known, but the user consistently receives an unexplained access error.",
],
},
},
} as const;
Several choices here are deliberate:
- The machine-readable keys are stable identifiers. Code, analytics, fixtures, and dashboards may depend on them, so rename them cautiously.
- The
whatfield defines membership. - The
not_forfield separates neighboring options. - The examples provide representative language, but they do not replace the boundary definitions.
- The question asks for one first handling path, not every fact about the ticket.
human_reviewis specific: it is not “anything confusing,” but a known safe disposition for unsupported or contradictory evidence.
The arbitrary field names such as what, not_for, and examples are useful structure, not special API keywords. The important part is that the same field names appear consistently across criteria, making comparisons clear.
Make the result usable without model-shaped business logic
A Choice becomes operational only when the rest of the application handles every value explicitly. Keep that mapping in ordinary deterministic TypeScript.
type SupportAction =
| "create_account_recovery_task"
| "send_incident_update"
| "route_to_standard_support"
| "create_human_review_task";
const actionForDisposition: Record<SupportDisposition, SupportAction> = {
account_recovery: "create_account_recovery_task",
incident_update: "send_incident_update",
standard_support: "route_to_standard_support",
human_review: "create_human_review_task",
};
This small Record provides a useful compile-time check: adding an option to the Choice requires you to decide what the application does for it. That is much safer than a broad fallback such as:
// Avoid this pattern.
return "route_to_standard_support";
for an unexpected or newly introduced category.
There are two independent reasons a ticket may go to review:
- Jev chooses
human_reviewbecause the evidence itself does not support an automated disposition. - Jev chooses a normally actionable path, but the returned confidence is below your automation threshold.
Keep these concepts separate. The first is an explicit semantic outcome of the Choice. The second is a risk-control policy applied to any selected value. You already encountered Jev’s probabilities and confidence in Week 1; Week 4 will cover how to choose and validate thresholds from real evaluation data.
For now, the key design point is simpler: an option cannot be considered actionable unless your deterministic code has a defined action for it.
Validate the boundary before using real traffic
Before sending live tickets, review the Choice with a compact decision table. These are not merely examples for Jev; they are candidate cases for an evaluation dataset later in the course.
| Evidence pattern | Intended option | Why |
|---|---|---|
account_access: "locked"; report says sign-in is blocked | account_recovery | An authoritative account restriction supports a dedicated workflow. |
| Account is active; relevant incident is active; report matches incident impact | incident_update | The incident path can communicate current known status safely. |
| Account is active; no incident; report asks how to change a profile preference | standard_support | No current core-function blocker is claimed. |
| Account status is unknown; report says the core workspace is inaccessible | human_review | Automation lacks necessary evidence. |
| Account is active; no incident; report says a core function is inaccessible with an unexplained error | human_review | A blocker exists, but none of the automated explanations applies. |
Use this review sequence:
- Pairwise check: compare every pair of options. Can a realistic state satisfy both criteria? If yes, add a boundary or deterministic precedence rule.
- Coverage check: collect representative real tickets and identify which option each should reach. Unclassified cases must get an intentional safe path.
- Action check: verify that each value has exactly one defined first action.
- Evidence check: confirm that every condition in the criteria has corresponding state evidence. Do not ask Jev to infer whether an incident is active if your incident system already knows.
- Vocabulary check: use terms that match your product and operations language. “Core function” must be defined somewhere concrete for the team operating the workflow.
For a quick implementation-oriented illustration of Choices selecting an allowlisted action, watch this short segment:
In “wtf is jev?” from Syntax, the presenter demonstrates using Jev to classify intent and select a deterministic function rather than generate a response.
Watch function selection. Focus on the architectural separation: Jev identifies the bounded intent and relevant parameters, while ordinary application code invokes the selected function. Apply the same separation to account_recovery, incident_update, standard_support, and human_review.
A Choice question is a compact interface between ambiguous language and deterministic software. A good one has a single decision axis, option definitions that exclude one another, deliberate coverage for unhandled evidence, and a concrete action for every possible selected value.
For the support example, the state supplies evidence; the Choice selects exactly one first handling path; and TypeScript maps that path to a known action. The model judges the bounded ambiguity, while policy, side effects, and safe fallbacks remain in your code.
Next, you will define a Score question: an ordered set of levels anchored by observable criteria, useful when the decision is about degree rather than category.
Can't find a good explanation? Sign up and we'll make it for you
Sign up