Skip to content
AI Architecture
12 min readKemi Okoro

Typed Decisions for AI Agent Workflows: Record Before Routing

A typed model answer is an inference result, not a workflow transition. Record it once, pin the policy that consumes it, and replay the same decision without silently asking again.

Hypothetical ticket flows to committed D-42, then policy routes it; replay returns to D-42 instead of a fresh inference.

Hypothetical flow: a committed inference feeds policy, and replay reuses the same record.

Short answer: Typed decisions for AI agent workflows should be stored as one completed, versioned inference event. Let deterministic workflow policy consume that record and choose an allowed route, retry, stop, or escalation. On replay, consume the committed event and the policy version used for its transition. An intentional reassessment gets a new event. This decision-record pattern is an engineering proposal, not a feature Jev or Temporal promises to provide for every application.

A support ticket says the invoice is wrong and service ends tomorrow. A worker asks a model whether the case is urgent. It gets a typed answer, then restarts before routing the ticket. If the restarted worker asks again, it may get another answer for the same case. This is a hypothetical failure mode, not a report of a production incident.

Temporal documents a specific replay rule: workflow code must make the same workflow API calls in the same sequence for the same input, and non-deterministic operations such as LLM calls belong in Activities [4]. A completed Activity result recorded in Temporal history is reused on replay; it does not spontaneously change. The risk in our hypothetical system is an uncommitted call or an application-level re-request disguised as a retry.

The retry that turns one ticket into two decisions

The critical instant is between receiving an answer and committing what the workflow will treat as settled. In the hypothetical ticket, the first worker sees an urgency assessment and crashes. A replacement worker can do one of three quite different things: read an already recorded result, retry an inference that never completed durably, or call the model again despite an existing result. Only the third silently turns the same step into a new decision.

Temporal's documentation is useful precisely because it separates replay from fresh external work. A workflow replay uses recorded history; an Activity is the boundary for non-deterministic calls, including AI invocations [4]. If a completed Activity result is in history, replay does not need a new model answer. If the attempt did not commit a completed result, an Activity retry can run again and might return different content. That is an inference-boundary problem to plan for, not proof that Temporal replay itself changes completed answers.

Outside Temporal, treat this as a design inference. Suppose a worker receives a queue job, gets ticket-42: urgent, stores the result only in memory, and crashes before acknowledging the job. After restart, the queue may redeliver the step, but it offers no committed answer to read. The worker cannot tell whether the first inference happened, whether it should spend a retry, or whether a customer-facing effect already followed. A type checker can validate each response independently and still miss this fork.

I would make the boundary explicit: an inference attempt is pending until its result is durably committed under a stable decision identifier. Policy consumes only committed results. If a call returns but commit fails, reconcile the attempt and do not assume it was already an accepted decision. A retry may produce a different answer, so consequential effects wait for the accepted record. The next question is what, exactly, that record contains.

What typed decisions fix in AI agent workflows

Cloudflare's Jev documentation shows a useful narrow interface. Given a support case, its example asks Noul for urgency, Choice for department, and Score for customer frustration. The illustrative response includes is_urgent: 0.95, department: billing with confidence 0.8, and model version jev-1.13.0 [3]. These are documentation values, not observed correctness rates or safe routing cutoffs.

A probabilistic decision model can turn a fuzzy phrase such as “service ends tomorrow” into the requested answer shape. That can spare downstream code from parsing free-form prose. TypeSafe describes Jev's outputs as typed and its probabilities as calibrated; those are the vendor's descriptions, not independent validation on your tickets [1]. The API example tells us what fields an application can receive. It cannot tell us whether this particular customer really faces an urgent service interruption.

Four questions are easy to collapse here. Does the response fit the schema? Is its interpretation of the ticket correct? Does a reported probability track outcomes on the cases this team sees? Who is allowed to move a ticket, retry an assessment, or send a message? Typing addresses the first question. Evaluation must address the next two. Application policy owns the last one. Treating 0.95 as an instruction to send an alert would cross all three remaining boundaries without evidence.

For fixed rules, skip inference altogether. An invoice total should be computed from records, not estimated from a frustration score. The neighboring guide on classifying deterministic work and model judgment covers that broader routing choice. Here the model has one job: interpret ambiguous ticket language. Even if it returns the right type, a reviewer still needs to know which text, question, and version produced the answer.

Make the inference a decision record

Here is a deliberately illustrative record for the ticket. The identifiers, times, hash, answer, and versions below are invented for the exercise. jev-1.13.0 is a model-version string shown in Cloudflare's example, not a claim that this fictional ticket was processed by Jev [3].

Illustrative D-42 groups input, schema, model, and answer; a separate reducer uses policy P-3 to choose a transition.
Illustrative D-42 holds inference provenance; P-3 remains a separate workflow-owned policy.
json
{
  "decisionId": "D-42",
  "caseId": "ticket-42",
  "status": "committed",
  "inputRef": "ticket-42/revision-2",
  "inputHash": "example-hash-of-revision-2",
  "questionId": "support-urgency/v2",
  "schemaVersion": "urgency/v2",
  "modelVersion": "jev-1.13.0",
  "answer": { "urgent": true },
  "reportedProbability": null,
  "createdAt": "2026-09-23T10:30:00Z"
}

The companion transition record, owned by the workflow rather than the model, might say decisionId: D-42, policyVersion: P-3, transition: route, destination: review-queue, and effectKey: ticket-42/D-42/route. P-3 is pinned when the transition is made. It does not belong inside the model's answer. If the model response includes a probability, preserve the reported value and its meaning rather than swapping it for a made-up confidence scale. A null probability is preferable to inventing one for this example.

The case reference and input revision say what the model assessed. A hash is useful for checking that the snapshot has not changed, but a hash alone cannot reconstruct the ticket: retain an access-controlled snapshot or resolvable reference if later review needs the text. Do not put private customer prose in an open audit log. The question identifier and schema version let a future reader distinguish “is service at risk?” from a later question such as “does the message request a refund?” The model version says which inference implementation was used; the answer says what it returned. The creation time orders events, not the truth of their contents.

Status matters as much as fields. A pending attempt is not a committed decision. A superseded decision remains readable with a link to its successor and a reason; it should not be edited in place to pretend it always said something else. None of this schema is specified by Cloudflare or Temporal. It is my proposed application record for making the replay rule inspectable [4]. For a related, wider lesson on keeping evidence outside an agent session, see durable handoff contracts for AI coding teams. A durable handoff still needs a consumer that cannot confuse the stored assessment with an order.

Let a deterministic policy reducer choose the transition

A reducer is a small function that takes the committed record, pinned policy, and current workflow state. Its output is a proposed transition from a fixed set. It neither asks the model another question nor sends the customer message itself. TypeSafe's Jev 1.13 limitations documentation recommends using code for arithmetic and structural invariants; that is a vendor recommendation, not a standard for this reducer [2].

For D-42, an illustrative policy might allow route only when the schema and input revision are supported, the case is still open, and the workflow has the relevant permission. Otherwise it chooses escalate or stop. It could route an admissible urgent: true answer to a human review queue without any probability threshold. That queue is a teaching example, not a default production routing recommendation.

text
reduce(record, policyVersion, workflowState):
  require policyVersion == workflowState.pinnedPolicyVersion
  if record.status != "committed": return stop("no settled inference")
  if record.schemaVersion not in policyVersion.supportedSchemas:
    return escalate("unsupported assessment schema")
  if record.inputRef != workflowState.currentTicketRevision:
    return escalate("ticket changed since assessment")
  if not workflowState.mayRoute:
    return escalate("route not permitted")
  if record.answer.urgent == true:
    return route("review-queue", effectKey(record.decisionId))
  return route("ordinary-queue", effectKey(record.decisionId))

This pseudocode omits actual policy loading and persistence; P-3 stands for an immutable, available policy definition. If a system cannot retrieve that definition during replay, a version label alone will not reproduce the transition. The reducer should also reject an answer that fails the pinned schema, not merely an unfamiliar schema name. Unsupported values need an abstain or escalation branch rather than a guessed route. If a team wants a confidence cutoff, it must derive and test one for its own labeled ticket distribution, define what the probability means, and pin the rule in the policy version. No number in Cloudflare's sample response can substitute for that work [3].

Retry budgets and permissions arrive from workflow state and application policy, never from record.answer. A retry return should specify what may be retried and why. Retrying the route effect is different from ordering a new semantic assessment. This distinction keeps a typed answer from expanding its own authority. Yet a reproducible transition can still duplicate an external effect if the worker fails after dispatch.

Replay the ticket, then change the question on purpose

Trace the fictional ticket twice. On the first successful pass, commit inference D-42 for ticket-42/revision-2. The reducer runs under P-3, proposes route(review-queue), and the dispatcher records the effect key ticket-42/D-42/route. On a workflow replay, the worker reads the committed answer and the pinned policy instead of asking the model to interpret revision 2 again. A completed Temporal Activity already has its result recorded in workflow history; the portable D-42 application record and policy pairing are an additional proposed design, not a Temporal requirement [4].

Replay reuses committed D-42 for revision 2; changed revision 3 creates D-43, while external effects use a separate dedup key.
Temporal documents replay from recorded history; this proposed pattern reuses D-42, creates D-43 for changed input, and deduplicates effects separately.

Now let the customer correct the ticket: service does not end tomorrow. The case has revision 3. Running D-42 through the reducer should hit the input-revision mismatch, not quietly reuse its old urgency answer for changed evidence. An authorised reassessment can append D-43, linked to D-42, with a new input reference, question version if it changed, reason for reassessment, and its own result. Keep the old transition visible. Changing policy intentionally is also a new transition event under a named policy version; it is not a retrospective rewrite of P-3.

One ugly case deserves its own line. If the model returned a response but the result was not committed, the worker cannot claim D-42 existed as a settled inference. Reconcile using the runtime's actual attempt/history semantics, then retry only under an explicit budget if there is no committed result. A new inference may differ. Do not attach a consequential effect to an answer that survived only in a dead worker's memory.

Inference replay and effect idempotency are separate jobs. Reusing D-42 prevents an unnoticed fresh assessment; it does not prevent a dispatcher from sending the same route or customer message twice after a timeout. Give the external operation a stable effect key and use a destination that deduplicates it, or reconcile its status before retrying when deduplication is unavailable. The hypothetical ticket-42/D-42/route key is illustrative. A stable record makes the branch explainable; the effect boundary still decides whether execution is safe to repeat.

Know when the model's score should not move the workflow

TypeSafe's Jev 1.13 documentation says score levels have weak numerical calibration and advises using code for exact math [2]. That disclosure makes a support-case Score useful as an ordinal signal to examine, not a measured refund amount or a precise level of customer distress. TypeSafe also publishes workflow evaluations whose reference labels, by its own account, average answers from GPT-6 Astra and Claude Fable 5.1 [6]. Agreement with those reference labels is not the same as correctness against independently adjudicated ticket outcomes.

The research on confidence needs equally careful boundaries. Xiong and coauthors report overconfidence in verbalized confidence in the general LLMs and tasks they studied [5]. That is a reason to test probability-bearing systems on the target task, not evidence that Jev has the same failure profile. The presence of a field named confidence does not make a team's threshold validated. Keep schema validity, semantic correctness, probability calibration, and permission to act separate in the test plan.

For the billing ticket, I would compare decisions against representative, independently reviewed cases before allowing a probability to change a consequential route. Include changed tickets, conflicting dates, and ambiguous service language. Run the candidate assessment in shadow mode against the existing queue decision; examine misses by case type and the cost of escalation. Until the evidence is adequate, let uncertain or unsupported answers go to review. Those are recommended tests, not reported Jev results or a claim that one threshold works across teams.

There is a simpler objection worth accepting. If refund eligibility follows an exact invoice rule, compute it in code. If interpreting ambiguous text genuinely helps, a general LLM constrained to structured output is also an option. Compare it with a specialized decision model on the same labeled cases, latency, cost, and failure modes rather than assuming the word “typed” wins the comparison. Whichever model answers, neither owns the retry budget or the action permission. The record-and-reducer boundary survives the model choice.

Apply the record to one retry boundary

Pick one semantic step, perhaps the urgency assessment for ticket-42. Write down the input revision and question version, the committed result identifier, and the policy version that consumed it. Then simulate a worker restart after the model returns but before routing. Can the restarted worker read the settled result, or does it ask again because no result was committed? Simulate a second restart after dispatch. Does the effect key prevent a duplicate route?

Change the ticket to revision 3 and run the same exercise. The expected distinction is concrete: replay uses D-42 for revision 2; a deliberate reassessment creates linked D-43 for revision 3; an unsupported schema or changed input goes to an explicit review branch. If any step silently writes a new answer under D-42, the workflow has lost the history needed to explain its own action. Temporal's replay discipline illustrates why that matters, while this record and reducer remain a proposed way to enforce the distinction in application state [4]. This is the practical contract for typed decisions in AI agent workflows: replay consumes the committed record, while a deliberate reassessment creates a new event.

Try the D-42 replay test on one decision step, then share which field or policy branch your team had to add.

References

  1. TypeSafe AI, "Introducing System One Models & Jev." https://typesafe.ai/blog/introducing-system-one-models-and-jev . Vendor announcement published 15 September 2026; its descriptions of typed outputs and calibration are vendor claims, not task-specific validation.
  2. TypeSafe AI, "Jev 1.13 jaggedness." https://docs.typesafe.ai/model-jaggedness/jev-1.13 . Vendor limitations documentation, last reviewed 17 September 2026; cited for disclosed score calibration limits and recommendations to put exact math and invariants in code.
  3. Cloudflare Workers AI, "Jev model documentation." https://developers.cloudflare.com/ai/models/typesafe/jev/ . API examples of question types, example answer fields, and model version; sample probabilities are not accuracy measurements.
  4. Temporal, "Workflow Definition." https://docs.temporal.io/workflow-definition . Temporal-specific documentation on deterministic workflow replay and putting LLM/AI calls in Activities. The portable decision-record schema in this article is an engineering proposal.
  5. Xiong et al., "Can LLMs Express Their Uncertainty? An Empirical Evaluation of Confidence Elicitation in LLMs," ICLR
2024. https://arxiv.org/abs/2306.13063 . Study of general LLM confidence elicitation, not a Jev calibration test.
  1. TypeSafe AI, "Workflow Evals." https://evals.typesafe.ai/ . Vendor evaluation site describing model-generated reference labels and harness assumptions; agreement with those labels is not independent outcome validation.