Welcome to Week 2. This module focuses on the quality of the input you give Jev: first the state that contains evidence, then the atomic questions that judge that evidence. A good state payload is small enough to be legible, rich enough to support the decision, and deliberately scrubbed of data Jev does not need.
This lesson establishes the boundary between your application’s full internal data model and the decision-specific JSON object sent to Jev. By the end, you should be able to build a payload for a concrete judgment while excluding distractors, identifiers, secrets, and incidental history.
State is evidence, not your application state
A Jev call has three parts:
- State: the facts and evidence to be judged.
- Questions: bounded judgments about that evidence.
- Answers: typed results, including probabilities or confidence information depending on the question type.

For a front-end analogy, do not think of Jev state as your Redux, Zustand, or server-side domain store serialized wholesale. It is closer to a deliberately selected view model for one decision. Your internal ticket may have hundreds of fields; the model should receive only the subset that could reasonably change the answer.
Suppose a support system needs to answer:
“Is the customer currently unable to access a core product function?”
The full ticket record may contain:
- ticket and account IDs;
- customer name, email, and billing address;
- payment history;
- browser fingerprint and IP address;
- entire conversation history;
- internal agent notes;
- product area;
- the customer’s latest report;
- the current account-access status;
- known incident status.
Only some of these bear on an access-blocker judgment. Sending everything is not “safer because the model has context.” It introduces two distinct problems:
- Accuracy risk: irrelevant detail becomes a distractor.
- Privacy and security risk: data is exposed without a decision-specific reason.
A practical relevance test is simple:
For each proposed field, complete: “This field can change the decision because…”
If you cannot complete that sentence precisely, remove the field. A second test works in the opposite direction:
For every important noun or condition in the question, identify the state field that supplies its evidence.
For “currently unable to access a core product function,” useful state may include report, product_area, account_access, and known_service_incident. A customer’s name does not establish access failure. Neither does their current payment method unless the decision specifically concerns billing-related access restrictions.
Jev 1.13 jaggedness - TypeSafe AI
Read TypeSafe AI’s guidance on why oversized, unfocused state harms decision quality. It provides the central operational rule for this module: filter data in application code before making the Jev call.
In the subsection “Large state full of irrelevant detail,” read the discussion of irrelevant state. Also note the preceding “Indirection” subsection: later, when you write questions, name the relevant state fields directly rather than forcing multi-step interpretation. The token limits are useful constraints, but the more important point is that staying under a limit does not make unrelated context helpful.
The documentation calls this failure mode “context rot”: irrelevant material costs accuracy. The remedy is not a better prompt around a bloated payload. It is a better payload builder.
Start from the decision contract
Build the state after you define the decision, not before. A reliable sequence is:
- Write one precise judgment in plain language.
- List the evidence needed to make that judgment.
- Compute exact facts in deterministic code.
- Remove unrelated fields.
- Transform sensitive fields that remain.
- Validate the resulting payload before it reaches the Jev client.
Consider this decision contract for a support workflow:
| Element | Decision-specific definition |
|---|---|
| Judgment | Whether the user is blocked from a core function now |
| Core functions | Authentication, workspace access, document editing, or paid API access |
| Evidence | Sanitized problem report, affected product area, access status, known incident status |
| Deterministic facts | Whether an account is locked; whether a relevant service incident is active |
| Exclusions | Identity, contact details, billing history, raw identifiers, unrelated prior tickets |
| Safe fallback | If evidence is missing or privacy filtering fails, do not automate the decision |
The resulting state can be compact:
{
"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"
}
Notice what has happened before Jev sees this object:
- The application determined the account’s lock status using its own authoritative data.
- The application checked the operational incident system.
- The customer’s account ID was used locally to retrieve those facts, but it was not included in the state.
- The report preserves meaningful evidence, such as the error code and affected function, while direct identifiers have been removed.
This distinction matters. Jev is appropriate for judging language and bounded ambiguity in the report. It should not be asked to infer a known account lock from a raw event log or calculate whether a status record is current when your application can determine that exactly.
The same principle appears in a different domain in TypeSafe AI’s insurance example. A coverage judgment needs policy terms, incident description, dates, claimed amounts, and documentation status. It does not automatically need every field stored in the insurer’s system.
Self-consistency: nouls - TypeSafe AI
This cookbook presents an insurance claim as a structured Jev state. Use it as an evidence-selection exercise: identify which parts of a policy and claim could support a coverage-related judgment, then distinguish them from identifiers or pre-existing conclusions that may not be needed.
In the section “The state: an auto-insurance claim, as JSON,” first read the scenario introduction. Then inspect the policy, claim, adjuster_notes, and claim_history objects in the JSON. Focus on how coverage terms, dates, line items, and the incident narrative serve as decision evidence. Ask whether each identifier would be necessary for a particular question, rather than treating the entire example as a universal payload template.
A state is therefore not merely “valid JSON.” It is a compact, decision-scoped evidence contract.
Classify fields before you serialize them
When reviewing a source object, put every field into one of four categories.
| Category | Treatment | Support-ticket example |
|---|---|---|
| Necessary evidence | Include, preferably with a clear field name | account_access: "locked" |
| Deterministic derivation | Compute locally, then include the result if relevant | known_service_incident: "none" |
| Irrelevant context | Exclude | customer lifetime value for an access-blocker judgment |
| Sensitive data | Exclude, redact, generalize, or pseudonymize only if genuinely necessary | email address, access token, payment number |
A field can belong to two categories at once. A customer report is usually necessary evidence, but it may contain personal data. In that case, preserve the decision-relevant meaning while reducing exposure.
For example:
| Raw source text | Better decision state |
|---|---|
| “I’m Maria Ivanova at maria@example.com. I cannot log into the workspace after reset.” | “I cannot log into the workspace after reset.” |
| “Card ending 4821 was charged, and now I cannot use the paid API.” | “A billing-related issue is reported, and the user cannot use the paid API.” |
| “My one-time code is 839204. It does not work.” | “The user reports that a one-time authentication code does not work.” |
Do not redact away the actual evidence. In the last row, the specific one-time code is a secret and must not leave your application, but the fact that authentication-code verification fails is central to the judgment.
Prefer meaningful derived facts
Generalization is often better than passing a sensitive raw value:
- Exact birth date becomes an age band, if an age policy is relevant.
- Exact transaction amount becomes a policy-relevant amount band, if the threshold is what matters.
- A person’s name becomes a role such as
requester,account_owner, orcoworker. - A raw device identifier becomes a local deterministic result such as
new_device_detected: true.
Do not generalize indiscriminately. If the exact date, amount, locale, or relationship changes the business rule, retain the minimum precision required for that rule.
Pseudonymization is not deletion
Sometimes the identity relationship itself matters. For example, a fraud workflow may need to know that the same unknown person appears in several documents, without exposing their name. A stable local pseudonym such as person_7 can preserve that relationship.
However:
- Keep the mapping between pseudonym and real identity in your own controlled system.
- Do not put the mapping in Jev state.
- Do not assume a pseudonym is anonymous or outside your organization’s privacy obligations.
- Avoid reversible masking if the judgment does not need restoration at all.
For a bounded Jev judgment, a structured answer normally means there is little reason to deanonymize model output. The application already knows which ticket it is processing; it can join the answer to the ticket locally.
Redaction is a defensive layer, not a reason to over-share
Text fields are difficult because they can contain both useful evidence and unexpected sensitive data. Use a layered approach:
- Avoid sending the field if it is not needed.
- Select only the relevant excerpt instead of full conversation history.
- Remove known secrets such as passwords, session cookies, API keys, one-time codes, and payment details.
- Detect direct identifiers such as email addresses and phone numbers.
- Replace identity with role information when the relationship, rather than the person, matters.
- Test the sanitizer with adversarial samples, not only clean support text.
The video below gives a useful overview of anonymization techniques. Its examples concern external LLM prompts generally, but the design trade-offs apply to any model-bound state: pattern matching is effective for regular formats, while names and free-form addresses are harder to handle consistently.
Anonymizing Sensitive Data in LLM Prompts
Watch “Anonymizing Sensitive Data in LLM Prompts” from Trelis Research for a concise framing of prompt-scrubbing and its limitations. The useful takeaway here is that sensitive-data handling is a pipeline design problem, not a single regex.
Watch the opening overview. Focus on the two approaches introduced: entity and pattern detection, and local-model-assisted anonymization. Notice the warning implied by the example: restoring transformed details can fail when formats change. For a Jev classification workflow, prefer avoiding reversible transformations unless your decision truly requires them.
A few engineering cautions:
- Regex is appropriate for highly structured patterns, but it will miss malformed or unusual data.
- Named-entity recognition can detect names and places, but can also produce false positives and false negatives.
- A local model can help with complex free text, but it introduces latency, operational complexity, and its own evaluation requirement.
- Never depend on redaction alone for policy compliance. Access controls, retention policy, encryption, vendor agreements, audit logging, and incident response remain separate concerns.
Most importantly, credentials and authentication material are never decision evidence. Remove them before logging, persistence, telemetry, and model submission.
Implement a payload builder as an explicit boundary
Keep state construction in a small, testable module. Do not assemble it ad hoc inside a route handler or UI component.
Here is a TypeScript sketch. The accountId is intentionally used only for local lookup. It cannot appear in the returned payload because it is not part of the return type.
type ProductArea =
| "workspace_access"
| "document_editing"
| "api_access"
| "unknown";
type AccountAccess = "active" | "locked" | "suspended" | "unknown";
type IncidentStatus = "active" | "none" | "unknown";
interface RawSupportTicket {
accountId: string;
customerName: string;
customerEmail: string;
latestCustomerMessage: string;
productArea: ProductArea;
paymentHistory: Array<{ amountCents: number; status: string }>;
internalNotes: string[];
}
interface AccessBlockerState {
report: string;
product_area: ProductArea;
account_access: AccountAccess;
known_service_incident: IncidentStatus;
}
interface StateDependencies {
redactForModel(text: string): string;
getAccountAccess(accountId: string): AccountAccess;
getIncidentStatus(productArea: ProductArea): IncidentStatus;
}
function buildAccessBlockerState(
ticket: RawSupportTicket,
deps: StateDependencies,
): AccessBlockerState {
const report = deps.redactForModel(ticket.latestCustomerMessage).trim();
if (report.length === 0) {
throw new Error("Cannot assess access blocker without a usable report");
}
return {
report,
product_area: ticket.productArea,
account_access: deps.getAccountAccess(ticket.accountId),
known_service_incident: deps.getIncidentStatus(ticket.productArea),
};
}
Three things are deliberately absent from AccessBlockerState:
customerNameandcustomerEmail, because identity is not evidence of access failure;paymentHistory, because this decision is not about a billing dispute;internalNotes, because they may contain irrelevant speculation, personal information, or prior conclusions that bias the judgment.
If the product requirement changes to “Is the account blocked because of an unresolved billing restriction?”, then the state contract should change. You might add a deterministic field such as:
billing_restriction: "active" | "none" | "unknown"
You still would not send the complete transaction ledger or card details. The state changes because the decision changed, not because more data happens to be available.
Treat the builder as a security and quality gate
At minimum, test this module for four properties:
- Allowlisting: the serialized object has only expected keys.
- Privacy: synthetic emails, phone numbers, tokens, and payment-like strings do not survive
redactForModel. - Evidence preservation: error codes, affected product areas, and access-failure language survive where appropriate.
- Fallback behavior: an unavailable sanitizer, missing report, or unknown required local lookup prevents an unsafe automated call.
Avoid logging raw payloads by default. For operational debugging, log non-sensitive metadata such as payload schema version, field names, character count, redaction count, request latency, and the final action. If payload inspection is needed during an incident, use a tightly controlled workflow rather than permanent broad logging.
A final preflight checklist
Before sending state to Jev, verify:
- The decision is stated in one precise sentence.
- Every field can change the answer to that decision.
- Exact computations and authoritative lookups happened in code first.
- The payload contains no credentials, tokens, payment details, or unnecessary direct identifiers.
- Free text has been reduced to the relevant excerpt and passed through a tested privacy filter.
- Unknown is represented explicitly when it is meaningful; do not silently turn missing evidence into
false. - Field names are direct enough that a later question can refer to them unambiguously.
- The payload has runtime validation and safe observability.
The core idea is straightforward: send evidence, not records. A well-designed Jev state is focused on one judgment, includes deterministic facts computed by your application, and reduces or removes sensitive material before the request is made. Smaller state is not merely cheaper or tidier; TypeSafe AI explicitly warns that unrelated context reduces judgment quality.
Next, you will use such a state to define a Choice question with mutually exclusive, operationally actionable options.
Can't find a good explanation? Sign up and we'll make it for you
Sign up